关于虚析构函数和构造函数讨论(virtual constructors)

关于虚析构函数和构造函数讨论(virtual constructors)

一、相关日志

1、构造函数和析构函数

http://blog.163.com/zhoumhan_0351/blog/static/3995422720100250413207

2、继承和派生(一)

http://blog.163.com/zhoumhan_0351/blog/static/3995422720100284731826/

3、C++基础笔记(二)

http://blog.163.com/zhoumhan_0351/blog/static/39954227201012471245444/

析构函数可以是虚函数,而且通常声明为虚函数。如

void Perfect(Base* pHeap)

{

//...

delete pHeapObject;

}   

其中pHeap是传来的对象指针,它或者指向基类对象,或者指向子类对象。将析构函数声明为虚的,则在招执行delete pHeapObject时,会自动调用相应的析构函数。

4、多态性与虚函数

http://blog.163.com/zhoumhan_0351/blog/static/3995422720100290234430/

二、讨论

1、可以把析构函数定义为虚的,而且建议这么做。但是不能把构造函数声明为虚函数。

如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,系统会只执行基类的析构函数,而不执行派生类的析构函数。

解决的方法是将基类的析构函数声明为虚函数,这样由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。

构造函数不能声明为虚函数。这是因为在执行构造函数时类对象还未完成建立过程,当然谈不上函数与类对象的绑定。

凡是包含纯虚函数的类都是抽象类(因为纯虚函数是不能被调用的),包含纯虚函数的类是无法建立对象的。

//: C15:VirtualDestructors.cpp

// Behavior of virtual vs. non-virtual destructor

#include <iostream>

using namespace std;

class Base1 {

public:

  ~Base1() { cout << "~Base1()\n"; }

};

class Derived1 : public Base1 {

public:

  ~Derived1() { cout << "~Derived1()\n"; }

};

class Base2 {

public:

  virtual ~Base2() { cout << "~Base2()\n"; }

};

class Derived2 : public Base2 {

public:

  ~Derived2() { cout << "~Derived2()\n"; }

};

int main() {

  Base1* bp = new Derived1; // Upcast

  delete bp;

  Base2* b2p = new Derived2; // Upcast

  delete b2p;

} ///:~

我们可以看到,将基类析构函数声明为虚函数的好处,这样派生类也自动成为虚函数,在删除对象时,可以用基类指针干净彻底的销毁派生类对象。

2、虚函数用于动态联编,是在运行是通过类型来确定函数的调用,而构造函数是在内存分配之前调用的,不可能知道是哪个类型,所以不能为虚函数。

在继承体系中,构造的顺序就是从基类到派生类,其目的就在于确保对象能够成功地构建。构造函数同时承担着虚函数表的建立,如果它本身都是虚函数的话,则虚函数表不能成功建立。

那么,为什么C++不干脆把析构函数规定成虚函数?那是因为:虚函数意味着晚捆绑,而晚捆绑效率不高。另外,如果没有虚函数,则虚析构函数也是不必要的。

如果有虚成员函数,析构函数应该是虚的。

纯虚拟函数是用来提供函数接口的,虚拟函数是用来提供函数接口和默认的函数操作,非虚拟函数是用来提供函数操作的。而且一般不要在子类中重定义一个非虚拟函数。

3、当创建一个包含有虚函数的对象时,必须初始化它的VPTR以指向相应的VTABLE。

编译器插入隐藏代码到我们的构造函数中,这些隐藏代码不仅初始化VPTR,而且还检查this指针(以防止operator new返回零)和调用基类构造函数。当我们调整代码时,要去掉这些影响程序效率的内联构造函数。

如果在构造函数中调用一个虚函数,被调用的只是这个函数的本地版本,虚机制在构造函数中不工作。因为虚函数机制使得可以调用派生类中的函数,而有可能派生类对象(的函数)还没有初始化,这将出问题。 The state of the VPTR 

is determined by the constructor that is called last. while all this series of 

constructor calls is taking place, each constructor has set the VPTR to its 

own VTABLE. If it uses the virtual mechanism for function calls, it will 

produce only a call through its own VTABLE, not the most-derived 

VTABLE (as would be the case after all the constructors were called).

You should keep in mind that constructors and destructors are the

 only places where this hierarchy of calls must happen 

4、纯虚析构函数

必须为纯虚析构函数提供一个函数体。同时,不要求在派生类中提供纯虚函数的定义,因为编译器会自动定义,所以派生类不会成为抽象类。析构函数的纯虚性惟一效果是阻止基类的实例化。

//: C15:UnAbstract.cpp

// Pure virtual destructors 

// seem to behave strangely

class AbstractBase {

public:

  virtual ~AbstractBase() = 0;

};

AbstractBase::~AbstractBase() {}

class Derived : public AbstractBase {};

// No overriding of destructor necessary?

int main() { Derived d; } ///:~

下面是一个例子:

//: C15:PureVirtualDestructors.cpp

// Pure virtual destructors

// require a function body

#include <iostream>

using namespace std;

class Pet {

public:

  virtual ~Pet() = 0;

};

Pet::~Pet() {

  cout << "~Pet()" << endl;

}

class Dog : public Pet {

public:

  ~Dog() {

cout << "~Dog()" << endl;

  }

};

int main() {

  Pet* p = new Dog; // Upcast

  delete p; // Virtual destructor call

} ///:~

As a guideline, any time you have a virtual function in a class, you 

should immediately add a virtual destructor (even if it does nothing).

 This way, you ensure against any surprises later.

Virtual or not. Inside a destructor, only the “local” version of the 

member function is called; the virtual mechanism is ignored.

//: C15:VirtualsInDestructors.cpp

// Virtual calls inside destructors

#include <iostream>

using namespace std;

class Base {

public:

  virtual ~Base() { 

cout << "Base1()\n"; 

f(); 

  }

  virtual void f() { cout << "Base::f()\n"; }

};

class Derived : public Base {

public:

  ~Derived() { cout << "~Derived()\n"; }

  void f() { cout << "Derived::f()\n"; }

};

int main() {

  Base* bp = new Derived; // Upcast

  delete bp;

} ///:~

为什么会这样呢?因为考虑到,虚析构函数调用派生类对象的虚成员函数,而有可能派生类对象的该成员函数它已经删除了,因此编译器决定只调用本地版本。Notice that the same is true(指构造函数不能为虚函数,而虚机制在析构函数中不起用途) for the constructor and destructor, but in the 

constructor’s case the type information wasn’t available, whereas in the 

destructor the information (that is, the VPTR) is there, but is isn’t reliable.

6、基于对象的继承

删除一个void指针并不调用析构函数。

基于对象的继承:当创建一个类时,都会直接或间接地从一个公共基类中继承出它。

//: C15:OStack.h

// Using a singly-rooted hierarchy

#ifndef OSTACK_H

#define OSTACK_H

class Object {

public:

  virtual ~Object() = 0;

};

// Required definition:

inline Object::~Object() {}

class Stack {

  struct Link {

Object* data;

Link* next;

Link(Object* dat, Link* nxt) : 

  data(dat), next(nxt) {}

  }* head;

public:

  Stack() : head(0) {}

  ~Stack(){ 

while(head)

  delete pop();

  }

  void push(Object* dat) {

head = new Link(dat, head);

  }//前插法

  Object* peek() const { 

return head ? head->data : 0;

  }

  Object* pop() {

if(head == 0) return 0;

Object* result = head->data;

Link* oldHead = head;

head = head->next;

delete oldHead;

return result;

  }

};

#endif // OSTACK_H ///:~

 //: C15:OStackTest.cpp

//{T} OStackTest.cpp

//#include "OStack.h"

#include <fstream>

#include <iostream>

#include <string>

using namespace std;

// Use multiple inheritance. We want 

// both a string and an Object:

class MyString: public string, public Object {

public:

  ~MyString() {

cout << "deleting string: " << *this << endl;

  }

  MyString(string s) : string(s) {}

};

int main() {

  ifstream in("C:\\HowMany2.out");

  Stack textlines;

  string line;

  // Read file and store lines in the stack:

  while(getline(in, line))

textlines.push(new MyString(line));

  // Pop some lines from the stack:

  MyString* s;

  for(int i = 0; i < 10; i++) {

if((s=(MyString*)textlines.pop())==0) break;

cout << *s << endl;

delete s; 

  }

  cout << "Letting the destructor do the rest:"

<< endl;

} ///:~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值