构造函数不能为哪些情况
1、构造函数不能用const修饰
const修饰的其实是this指针,下面我们看一下this指针的类型:
- 普通成员函数:T* const------>this指针的指向不能被改变
- const成员函数:const T* const ----->this指针的指向不能被改变,this指向的内 容也不能被修改
构造函数功能就是初始化变量,需要在构造函数体中进行赋值,就需要更改其中内容,所以不能为const修饰。
2、构造函数不能是静态成员函数,也就是不能用static修饰
静态成员函数只能操作静态成员变量,如果构造函数可以被static修饰,那么构造函数就不能对非静态成员函数进行初始化了。
静态成员函数是属于整个类的,不是属于对象的,是所有对象共享的资源。
静态成员函数没有this指针,不能通过this指针来进行对象的调用。
3、构造函数不能为虚函数
- 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。
- 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。
虚函数表属于谁
虚指针(vptr):每个含有虚方法(虚函数)对象里有虚表指针,指向虚表。
虚函数表:虚函数表是顺序存放虚函数地址的,虚表是顺序表,表里存放了虚函数的地址。
C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。目前gcc 和微软的编译器都是将vptr放在对象内存布局的最前面。
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() {
cout << "Base::f" << endl; }
virtual void g() {
cout << "Base::g" << endl; }
virtual void h() {
cout << "Base::h" << endl; }
};
typedef void(*Fun)(void);
int main()
{
Fun pFun = NULL;
Base obj_1,obj_2;
// obj_1 虚函数表 — 第一个函数
pFun = (Fun)*((int*)*(int*)(&obj_1));
pFun();
pFun = (Fun)*(((int*)*(int*)(&obj_1))+1);
pFun();
// 输出 虚函数表地址 与 虚函数表元素 的值
cout << "obj_1 虚函数表地址:" << (int*)(&obj_1) << endl;
cout << "obj_2 虚函数表地址:" << (int*)(&obj_2) << endl;
cout << "obj_1 虚函数表 — 第一个函数地址:" << (int*)*(int*)(&obj_1) << endl;
cout << "obj_2 虚函数表 — 第一个函数地址:" << (int*)*(int*)(&obj_2) << endl;
return 0;
}
/*
输出 :
Base::f
Base::g
obj_1 虚函数表地址:0018FF40
obj_2 虚函数表地址:0018FF3C
obj_1 虚函数表 — 第一个函数地址:0046F0AC
obj_2 虚函数表 — 第一个函数地址:0046F0AC
Press any key to continue
*/
【结论】
- 虚函数表属于类,类的所有对象共享这个类的虚函数表。
- 不同对象虚函数表是一样的(虚函数表的第一个函数地址相同);
- 每个对象内部都保存一个指向该类虚函数表的指针vptr,每个对象的vptr的存放地址都不一样,但是都指向同一虚函数表。