在构造函数和析构函数中能调用虚函数吗?
严禁在构造函数中调用虚函数
1
2
3
4
5
6
7
8
9
10
11
12
13class A{
int data;
public:
virtual void dosome()const{};
A(){
dosome();
}
};
class B : public A{
public:
virtual void dosome(){};
};
B b;//往往会出现意想不到的错误- 如果在父类的构造函数中调用虚函数,当使用子类对象的时候,会首先调用父类的构造函数。
- 在父类构造函数执行过程中,this指针为base class对象,因此在dosome()函数中的操作是在父类对象中完成的。
- 由于虚函数dosome()在父类对象中完成,往往达不到多态(延迟绑定)的目的。
严禁在析构函数中调用虚函数
- 在子类对象析构时顺序为先将自身的local data释放,然后调用父类的析构函数,如果父类析构存在虚函数,那么编译器会有两种选择:调用虚函数的基类版本或者调用虚函数的派生类版本。
- 如果调用派生类版本的函数,此时子类部分已经释放,会导致严重错误。
- 实际情况是编译器会调用基类版本的虚函数,那么和构造函数一样,不会发生多态。
解决方式
- 将在构造或析构函数中调用的函数设置为non-virtual,要求子类对象在构造时传递必要的信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class A{
int data;
public:
void dosome(const string& info)const{};
A(const string& info){
dosome(info);
}
};
class B : public A{
private:
static string createParamter();
public:
virtual void dosome(){};
B():A(createParamter()){};
};
如果new、delete、delete[]没有成对使用会发生什么?
使用delete释放对象数组
1
2
3string* s = new string[100];
//do something
delete s;- 上述情况下,只会调用第一个s的函数,其余99个对象不执行任何操作,后面程序也不会再拿到其句柄,从而会造成内存泄露。
- 实际上,对于一组对象,delete[]会寻找数组前的一个count计数来获取数组长度。
使用delete[]释放单个对象
1
2
3string* s = new string;
//do something
delete[] s;// Exception has occurred.Segmentation fault- delete[]在内存中读取s指针前4字节内存作为数组长度,这样做显然是非法的。
设计一个class需要注意什么?
- 对象创建和销毁(构造、析构函数、内存分配)
- 对象初始化和赋值
- 对象如果pass by value应该怎么做?(如果对象内存在指针,是否需要深拷贝)
- 定义class约束(数据类型检查)
- 继承或者被继承
- 类型转换(隐式类型转换)
- 操作符重载
- 标准函数是否适用(拷贝赋值,拷贝构造)