1.构造函数和析构函数的调用顺序? 析构函数为什么要虚拟?
//析构函数调用不当带来的内存泄漏
#include <iostream>
using namespace std;
class Base //基类定义
{
private: //字符指针
char* data;
public:
Base() //无参构造函数
{
data = new char[64]; //动态内存申请
cout<<"Base类构造函数被调用"<<endl;
};
<span style="color:#cc0000;">virtual</span> ~Base() //析构函数
{
delete [] data; //data指向的内存被释放
cout<<"Base类析构函数被调用"<<endl;
};
};
class Child : public Base //Child类由基类Base派生而来
{
private:
char* m_data; //增添的字符指针成员
public:
Child():Base() //构造函数,初始化表中执行基类的构造函数
{
m_data = new char[64]; //动态申请内存,并将首地址赋给m_data
cout<<"Child类构造函数被调用"<<endl;
};
~Child() //析构函数
{
delete [] m_data; //内存资源释放
cout<<"Child类析构函数被调用"<<endl;
};
};
int main()
{
Base *pB = new Child; //动态申请了一块Child大小的内存,赋给Base基类指针
delete pB; //基类析构函数执行
return 0;
}
【解析】:
// Base类构造函数被调用
// Child类构造函数被调用
// Base类析造函数被调用
//Child类的析构函数没有被执行。(如果没加virtual)
这是因为上述代码的析构函数是非虚的,//释放时会造成child类的析构函数得不到执行,从而导致内存泄漏//解决方法:将Base类的析构函数申明为虚函数即可. 即在 ~Base() 前面加上 virtual
2.多基派生引起的虚函数访问二义性问题
#include <iostream>
using namespace std;
class A
{
public:
virtual void a() //虚函数
{
cout << "a() in A" << endl;
}
virtual void b() //虚函数
{
cout << "b() in A" << endl;
}
virtual void c() //虚函数
{
cout << "c() in A" << endl;
}
};
class B
{
public:
virtual void a() //虚函数
{
cout << "a() in B" << endl;
}
virtual void b() //虚函数
{
cout << "b() in B" << endl;
}
void c() //非虚函数
{
cout << "c() in B" << endl;
}
void d() //非虚函数
{
cout << "d() in B" << endl;
}
};
class C:public A, public B
{
public:
virtual void a() //虚函数,覆盖
{
cout << "a() in C" << endl;
}
void c() //特殊
{
cout << "c() in C" << endl;
}
void d() //非虚函数,隐藏
{
cout << "d() in C" << endl;
}
};
int main()
{
C c; //声明一个派生类对象c
// c.b(); //b()在A, B类中都定义为虚函数, C中无法确定使用哪个版本, 引起二义性错误
cout << "c.b();会引起二义性错误" << endl;
cout << endl;
A* pA = &c; //用派生类对象obc的地址为A类指针赋值
pA->a(); //a()在A, B , C三个类中都是虚函数, 调用C类的c(), 输出: a() in C
pA->b(); //b()在A, B类中都是虚函数, C类中没有定义, 编译器无法确定使用哪个
//版本, 只能采用静态联编. 由于pA的类型是A *,所以输出: b() in A
pA->c(); //c()在A中为虚函数, B中为普通函数, C中进行了重定义. 此时输出取决
//于指针pA的类型A, 由于c()在A中为虚函数, 故按照虚函数的规则处理,输出c() in C
cout << endl;
B* pB = &c; //用派生类对象obc的地址为B类指针赋值
pB->a(); //a()在A, B , C三个类中都是虚函数, 调用C类的c(), 输出: a() in C
pB->b(); //b()在A, B类中都是虚函数, C类中没有定义, 编译器无法确定使用哪个
//版本, 只能采用静态联编. 由于pB的类型是B *,所以输出: b() in B
pB->c(); //c()在A中为虚函数, B中为普通函数, C中进行了重定义. 此时输出取决
//于指针pB的类型B, 由于c()在B中为普通函数, 故按照普通函数的规则处理,输出c() in B
pB->d(); //d()在B, C类中都定义为普通函数, C中的d()会隐藏基类B中的d(), 但pB类型为B *, 故输出d() in B
cout << endl;
C *pC = &c;
pC->a(); //a()在A, B , C三个类中都是虚函数, 调用C类的c(), 输出: a() in C
// pC->b(); //b()在A, B类中都定义为虚函数, C中无法确定使用哪个版本, 引起二义性错误
cout << "pC->b();会引起二义性错误" << endl;
pC->c(); //c()在A中为虚函数, B中为普通函数, C中进行了重定义(?). 此时输出取决
//于指针pC的类型C, c()在C中无论是虚函数还是普通函数, 都输出c() in C
pC->d(); //d()在B, C类中都定义为普通函数, C中的d()会隐藏基类B中的d(), 但pC类型为C *, 故输出d() in C
return 0;
}
【解析】:
前面在学习继承时也存在二义性,单基派生的二义性,多基继承的二义性,上面代码中产生的二义性,跟多基继承产生的二义性处理方式一致,//引起二义性错误. 解决: pC->B::b();
3.重载,覆盖,隐藏的区别
(1.)成员函数被重载的特征:
class A
{
……
virtual int fun();
void fun(int);
void fun(double,double);
……
};
1).相同的范围(同一个类中)
2).函数名相同
3).参数不同(参数个数·参数类型)
4).virtual关键字可有可无
(2.)覆盖是指派生类函数覆盖基类函数,特征是:
class A
{
public:
virtual void fun1(int, int) {}
virtual int fun2(char *){return 0;}
};
class B : public A
{
public:
void fun1(int, int){}
};
class C : public B
{
public:
int fun2(char *) {return 1;}
};
//B中的fun1覆盖了A中的fun1,同时继承了A中的fun2
//C类继承了B中的fun1,同时重定义覆盖了fun2
1).不同的范围(基类和派生类)
2).函数名相同
3).参数相同,返回类型相同
4).基类函数中必须有virtual关键字
(3.)隐藏,指派生类中的函数屏蔽了与其同名的基类中函数,特征:
class A //类A的定义
{
public:
void fun(int xp) //非虚成员函数fun,参数为int型
{
cout << xp << endl;
}
};
class B:public A //类B由类A派生而来
{
public:
void fun(char* s) //隐藏,oversee,参数为字符串
{
cout << s << endl;
}
};
1).函数名相同,参数也相同,但是没有virtual关键字(区别覆盖:virtual有无)
2).函数名相同,但参数不同,不论有无virtual关键字,基类中的同名函数即被屏蔽(区别重载:范围不同)