1。只有virtual member function,而没有virtual data member。
virtual member function通过vtbl得以实现,这使得具有
virtual member function的class之object负担额外的一个vptr。
看个例子:
class Base
{
public:
virtual void foo() {};
int b;
};
class Derived : public Base
{
public:
void foo() {};
int d;
};
如果写Base *p; ...; p->foo(),我们不能确定调用的是Base::foo()还是Derived::foo()。
这是由于foo()为virtual,p->foo()会被展开成(*(p->vptr)[i])(),其中i为foo()
之入口在vtbl中的索引,该例中为0。所以,具体调用哪一个,取决于p所指之object model
中的vptr(一般在该object的内存的开始处)到底指向那个vtbl,是Base的,还是Derived的。当然,vptr由编译器负责生成代码来设定,如果写Base *p = new Derived,他的vptr
将指向关于Derived的vtbl。
上面 superbigboy(风之影*备考中) 所讲的:
“最后,当执行delete pB后,自然要调用的是父类的析构函数了.”
(1)对于父类的dtor为non-virtual时正确的,但原因解释的不对。
如果一个member function是non-virtual的,那么对它的调用会被展开为一般函数的形式,
如p->fun(),而fun非虚,则会被展开成fun(p)的形式,同理,对dtor的调用也是如此。
(2)对于父类的dtor为virtual时是不正确的。
由前面关于virtual function的原理可知,这时delete pB;会调用Derived::~Derived(),
而编译器会在Derived::~Derived()中添加调用Base::~Base()的代码。
但是,对于data member,就没有virtual这种待遇了。所以就出现了
superbigboy(风之影*备考中) 所讲的用pB无法访问Derived的data member的问题。
但pB所指之内存的确有Derived的部分,试看下面例子:
class Base
{
public:
Base(int x, int y) : c(x), d(y) {}
virtual ~Base() { cout<< "~Base" << endl ; }
protected:
int c;
int d;
};
class Derived : public Base
{
public:
Derived(int x, int y, int z, int w) : Base(x,y), a(z), b(w) {}
~Derived() { cout<< "~Derived" << endl ; }
protected:
int a;
int b;
};
void main(void)
{
Base * pB = new Derived(1, 2, 3, 4);
int data_member[4];
memcpy(data_member, (int*)(pB) + 1, sizeof(data_member));
// (int*)(pB) + 1 是为了跳过vptr
copy(data_member, data_member + sizeof(data_member)/sizeof(int),
ostream_iterator<int>(cout, " "));
cout << endl;
delete pB;
}
运行结果为:
1 2 3 4
~Derived
~Base
Press any key to continue
2。Base * pB = new Derived; 的工作会被分解为两步:(1)用operator new分配
sizeof(Derived)大小的内存 (2)调用ctor在这块内存上构造对象。
delete p;的工作会被分解为两步:(1)p->dtor() (2)operator delete p;
superbigboy(风之影*备考中) 所讲的:
”delete pB是释放pB所指向的空间,即new Derived的中父类的那部分空间,“
这是是不对的。
在基类dtor非虚时,delete pB在第一步的确只会调用Base::~Base(),原因前面已经说过。
但在第二步中,operator delete p;会释放掉p所指的内存,而这块内存是
用operator new分配的,他当然会做一些簿记工作,以记录所分配之内存的大小。
operator delete会利用这些簿记释放掉内存。
请注意,析构和释放内存不是一回事,他们也不是一起完成的。
virtual member function通过vtbl得以实现,这使得具有
virtual member function的class之object负担额外的一个vptr。
看个例子:
class Base
{
public:
virtual void foo() {};
int b;
};
class Derived : public Base
{
public:
void foo() {};
int d;
};
如果写Base *p; ...; p->foo(),我们不能确定调用的是Base::foo()还是Derived::foo()。
这是由于foo()为virtual,p->foo()会被展开成(*(p->vptr)[i])(),其中i为foo()
之入口在vtbl中的索引,该例中为0。所以,具体调用哪一个,取决于p所指之object model
中的vptr(一般在该object的内存的开始处)到底指向那个vtbl,是Base的,还是Derived的。当然,vptr由编译器负责生成代码来设定,如果写Base *p = new Derived,他的vptr
将指向关于Derived的vtbl。
上面 superbigboy(风之影*备考中) 所讲的:
“最后,当执行delete pB后,自然要调用的是父类的析构函数了.”
(1)对于父类的dtor为non-virtual时正确的,但原因解释的不对。
如果一个member function是non-virtual的,那么对它的调用会被展开为一般函数的形式,
如p->fun(),而fun非虚,则会被展开成fun(p)的形式,同理,对dtor的调用也是如此。
(2)对于父类的dtor为virtual时是不正确的。
由前面关于virtual function的原理可知,这时delete pB;会调用Derived::~Derived(),
而编译器会在Derived::~Derived()中添加调用Base::~Base()的代码。
但是,对于data member,就没有virtual这种待遇了。所以就出现了
superbigboy(风之影*备考中) 所讲的用pB无法访问Derived的data member的问题。
但pB所指之内存的确有Derived的部分,试看下面例子:
class Base
{
public:
Base(int x, int y) : c(x), d(y) {}
virtual ~Base() { cout<< "~Base" << endl ; }
protected:
int c;
int d;
};
class Derived : public Base
{
public:
Derived(int x, int y, int z, int w) : Base(x,y), a(z), b(w) {}
~Derived() { cout<< "~Derived" << endl ; }
protected:
int a;
int b;
};
void main(void)
{
Base * pB = new Derived(1, 2, 3, 4);
int data_member[4];
memcpy(data_member, (int*)(pB) + 1, sizeof(data_member));
// (int*)(pB) + 1 是为了跳过vptr
copy(data_member, data_member + sizeof(data_member)/sizeof(int),
ostream_iterator<int>(cout, " "));
cout << endl;
delete pB;
}
运行结果为:
1 2 3 4
~Derived
~Base
Press any key to continue
2。Base * pB = new Derived; 的工作会被分解为两步:(1)用operator new分配
sizeof(Derived)大小的内存 (2)调用ctor在这块内存上构造对象。
delete p;的工作会被分解为两步:(1)p->dtor() (2)operator delete p;
superbigboy(风之影*备考中) 所讲的:
”delete pB是释放pB所指向的空间,即new Derived的中父类的那部分空间,“
这是是不对的。
在基类dtor非虚时,delete pB在第一步的确只会调用Base::~Base(),原因前面已经说过。
但在第二步中,operator delete p;会释放掉p所指的内存,而这块内存是
用operator new分配的,他当然会做一些簿记工作,以记录所分配之内存的大小。
operator delete会利用这些簿记释放掉内存。
请注意,析构和释放内存不是一回事,他们也不是一起完成的。