Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;:虚拟函数是如何解决的;这";指针范围问题?_C++_Virtual Functions_Mingw32 - Fatal编程技术网

C++ C++;:虚拟函数是如何解决的;这";指针范围问题?

C++ C++;:虚拟函数是如何解决的;这";指针范围问题?,c++,virtual-functions,mingw32,C++,Virtual Functions,Mingw32,(C++,MingW4.4.0,Windows操作系统) 代码中所有的注释,除了标签和,都是我的猜测。如果你认为我错了,请纠正我: class A { public: virtual void disp(); //not necessary to define as placeholder in vtable entry will be //overwritten when derived class's vtable entry is pr

(C++,MingW4.4.0,Windows操作系统)

代码中所有的注释,除了标签和,都是我的猜测。如果你认为我错了,请纠正我:

class A {
public:
   virtual void disp(); //not necessary to define as placeholder in vtable entry will be
                        //overwritten when derived class's vtable entry is prepared after
                        //invoking Base ctor (unless we do new A instead of new B in main() below)
};

class B :public A {
public:
   B() : x(100) {}
   void disp() {std::printf("%d",x);}
   int x;
};

int main() {
   A* aptr=new B;             //memory model and vtable of B (say vtbl_B) is assigned to aptr
   aptr->disp();              //<1> no error
   std::printf("%d",aptr->x); //<2> error -> A knows nothing about x
}
A类{
公众:
virtual void disp();//不需要在vtable条目中定义为占位符
//在之后准备派生类的vtable条目时覆盖
//调用基ctor(除非我们在下面的main()中使用新的A而不是新的B)
};
B类:公共A{
公众:
B():x(100){}
void disp(){std::printf(“%d”,x);}
int x;
};
int main(){
A*aptr=new B;//将B的内存模型和vtable(比如vtbl_B)分配给aptr
aptr->disp();//无错误
std::printf(“%d”,aptr->x);//错误->A对x一无所知
}
这是一个明显的错误。为什么不是一个错误?我认为这个调用所发生的是:
aptr->disp();-->(*aptr->*(vtbl_B+偏移到disp))(aptr)
aptr
中的参数是指向成员函数的隐式
指针。在
disp()
内部,我们将有
std::printf(“%d”,x);-->标准::printf(“%d”,aptr->x);与std::printf相同(“%d”,此->x)那么为什么不给出错误而给出错误呢

(我知道vtables是特定于实现的东西,但我仍然认为值得问这个问题)

规则是:

< C++ >动态调度只适用于成员函数不为成员变量的函数。>/P> 对于成员变量,编译器仅查找该特定类或其基类中的符号名称

在案例1中,通过获取
vpt
,获取适当方法的地址,然后调用适当的成员函数来确定要调用的适当方法。
因此,在静态绑定的情况下,动态调度本质上是一个
fetch调用
,而不是正常的
调用

在案例2中:编译器只在
this
的范围内查找
x
,显然,它找不到它并报告错误

virtual void disp(); //not necessary to define as placeholder in vtable entry will be
                     //overwritten when derived class's vtable entry is prepared after
                     //invoking Base ctor (unless we do new A instead of new B in main() below)
你的评论并不完全正确。虚拟函数是odr使用的,除非它是纯函数(反之不一定成立),这意味着您必须为它提供定义。如果你不想为它提供一个定义,你必须使它成为一个纯粹的虚拟函数


如果进行这些修改之一,则
aptr->disp()
工作并调用派生类
disp()
,因为派生类中的
disp()
重写基类函数。当您通过指向基类的指针调用基类函数时,基类函数仍然必须存在
x
不是基类的成员,因此
aptr->x
不是有效的表达式。

B::disp
中的
aptr
不同。
B::disp
实现将
这个
作为
B*
,就像
B
的任何其他方法一样。当您通过
A*
指针调用virtual方法时,它首先被转换为
B*
(这甚至可能会改变它的值,因此在调用过程中它不一定等于
aptr

也就是说,真正发生的是

typedef void (A::*disp_fn_t)();
disp_fn_t methodPtr = aptr->vtable[index_of_disp]; // methodPtr == &B::disp

B* b = static_cast<B*>(aptr);
(b->*methodPtr)(); // same as b->disp()
typedef void(A::*disp_fn_t)();
显示方法ptr=aptr->vtable[显示的索引];//methodPtr==&B::disp
B*B=静态_-cast(aptr);
(b->*方法ptr)(;//与b->disp()相同

有关更复杂的示例,请查看此帖子。这里,如果有多个
A
基可以调用相同的
B::disp
,则MSVC生成不同的入口点,每个入口点将
A*
指针移动不同的偏移量。当然,这是具体实施的;例如,其他编译器可能会选择将偏移量存储在vtable中的某个位置。

您感到困惑,在我看来,您来自更具动态性的语言

在C++中,编译和运行时是清晰分离的。程序必须先编译,然后才能运行(这些步骤中的任何一个都可能失败)


那么,回过头来看:

编译失败,因为编译是关于静态信息的
aptr
属于
A*
类型,因此可以通过该指针访问
A
的所有方法和属性。由于声明了
disp()
但没有
x
,因此对
disp()
的调用将编译,但没有
x

<> P> >代码> <代码>的失败是关于语义的,而这些是在C++标准中定义的。


进入
,它可以工作,因为
a
中有一个
disp()
声明。这保证了函数的存在(我要说的是,您实际上位于这里,因为您没有在
A
中定义它)

<> P>运行时发生的事情是由C++标准语义定义的,但标准没有提供实现指导。大多数(如果不是全部)C++编译器将使用每个类的虚拟表+每个实例的虚拟指针策略,并且在这种情况下,您的描述看起来正确。
但是,这是纯粹的运行时实现,它运行的事实不会追溯到程序编译的事实。

ok,但
被切片为只了解
A
。在成员函数内部,我认为调用解析为
this->whatEver
。让我困惑的是,由于所示的隐式传递的
this
与B对象的传递方式不同,机制是如何工作的?@ustulation:No,this
属于
B
类型,而调用的方法是
B::disp()
,当然,它可以访问
x
,这是它自己的成员。这里没有切片,真正的规则是C++,动态调度只用于VILT。