C++派生类中如何初始化基类对象
今天收到盛大的面试,问我一个问题,关于派生类中如何初始化基类对象,我在想派生类对于构造函数不都是先构造基类对象,然后在构造子类对象,但是如果我们在成员初始化列表先初始化派生类的私有成员,在函数内去调用基类的构造函数,能编译通过吗?或者当我们定义了基类的默认构造函数,而没有去在派生类的构造函数中显示的去调用基类的构造函数,会出现什么状况,我想派生类肯定会自动去调用基类的默认构造函数,那么析构函数又怎么样呢?我们都知道派生类的析构函数会先被调用,然后基类的析构函数后被调用,但是我不知道我们是否需要在派生类的析构函数中显示的去调用基类的析构函数吗?这个有待我去验证。
代码一:在派生类中成员初始化列表先初始化派生类的私有成员,不显示的调用基类的构造函数
#include <iostream> using namespace std; class Base { private: int n; public: Base(int m):n(m){ cout<<"constructor is called\n";} ~Base(){} }; class Derive:public Base { private: int n; public: Derive(int m):n(m) { } ~Derive(){} }; int main() { Derive* a = new Derive(10);return 0; }
结果:编译错误,error C2512: “Base”: 没有合适的默认构造函数可用
代码二:在派生类中成员初始化列表先初始化派生类的私有成员,显示的调用基类的构造函数
#include <iostream> using namespace std; class Base { private: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} ~Base(){} }; class Derive:public Base { private: int n; public: Derive(int m):Base(m),n(m) { } ~Derive(){} }; int main() { Derive* a = new Derive(10);return 0; }
运行结果:
代码三:在派生类中成员初始化列表先初始化派生类的私有成员,不显示的调用基类的构造函数,则会调用默认的构造函数
#include <iostream> using namespace std; class Base { private: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} ~Base(){} }; class Derive:public Base { private: int n; public: Derive(int m):n(m) { } ~Derive(){} }; int main() { Derive* a = new Derive(10); return 0; }
运行结果:
代码四:派生类析构函数的调用过程中会不会自动去调用基类的析构函数呢?答案是,肯定的,所以千万不要在派生类的析构函数中再去调用基类的析构函数,这种去释放已经释放的内存,系统是不允许的。
#include <iostream> using namespace std; class Base { private: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} ~Base(){ cout<<"Base distructor is called\n"; } }; class Derive:public Base { private: int n; public: Derive(int m):Base(m),n(m) { } ~Derive(){ cout<<"Derive distructor is called\n"; } }; int main() { Derive* a = new Derive(10); delete a; return 0; }
运行结果:
代码5:如果我们去试试在派生类的析构函数中去调用基类的析构函数,看看结果如何?当我想这么做的时候,我突然发现这个代码我写出来,因为我们都知道,对于C++的一个对象要么将对象分配在栈中,要么将对象分配在堆中,而对于分配在栈中的对象我们过程结束后,自动调用类的析构函数,而分配在堆中的对象,得我们去delete,但是你必须拿到指向对象在堆中内存的句柄,也就是指针,但是我发现不可能拿得到,除非你在派生类的构造函数中去new基类对象,但是又有个问题,在派生类构造函数中去new出这个基类对象,那么基类对象是派生类的局部变量,还是派生类继承而来的呢?我发现肯定是派生类的局部变量,那么也就是说,如果new出一个派生类对象,那么派生类本身的私有成员是在堆中,而继承而来的属性,也就是基类的东西分配的栈中,好吧,这样,难道派生对象在内存上竟然不是连续的?
#include <iostream> using namespace std; class Base { private: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} ~Base(){ cout<<"Base distructor is called\n"; } }; class Derive:public Base { private: int n; public: Derive(int m):Base(m),n(m) // 在这里构造继承属性,即派生类的基类部分 { new Base(m); //这个仅仅在派生类中创建了一个基类的变量而已 } ~Derive(){ cout<<"Derive distructor is called\n"; } }; int main() { Derive* a = new Derive(10); delete a; return 0; }
运行结果如下:
构造两次基类,一次构造派生类中的基类成分,还有一次仅仅是派生类的变量而已。同时析构派生类,自动调用基类析构函数。
代码六:在派生类构造函数中用new分配一个基类对象,然后析构掉,在main函数中去调用基类的成员函数,发现仍可调度,说明在派生类构造函数中用new分配基类对象,不是派生类的基类成分。
#include <iostream> using namespace std; class Base { private: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} void get(){ cout<<"get() is called\n"; } ~Base(){ cout<<"Base distructor is called\n"; } }; class Derive:public Base { private: int n; public: Derive(int m):Base(m),n(m) // 在这里构造继承属性,即派生类的基类部分 { Base* a =new Base(m); //这个仅仅在派生类中创建了一个基类的变量而已 delete a; } ~Derive(){ cout<<"Derive distructor is called\n"; } }; int main() { Derive* a = new Derive(10); a->get(); delete a; return 0; }
运行如下:
下面我就有一个疑问了,派生类中只能将基类成分分配在栈中吗?而如果去new出派生类对象,那么岂不内存不连续了,这是个问题?我验证了一下,与我的想法并非一致,内存还是连续的。只不过在派生类构造基类的过程中(new出派生类方式),只是在堆中去分配基类的成分,虽然使用非new来创建基类对象。
代码如下:
#include <iostream> using namespace std; class Base { public: int n; public: Base(){ cout<<"default constructor is called\n"; n = 8;} Base(int m):n(m){ cout<<"constructor is called\n";} void get(){ cout<<"get() is called\n"; } ~Base(){ cout<<"Base distructor is called\n"; } }; class Derive:public Base { private: int n; public: Derive(int m):Base(m),n(m) // 在这里构造继承属性,即派生类的基类部分 { cout<<"Base address: "<<&(Base::n)<<endl; //地址 cout<<"Derive address: "<<&n<<endl; //地址 } ~Derive(){ cout<<"Derive distructor is called\n"; } }; int main() { Derive* a = new Derive(10); delete a; return 0; }
运行结果如下:
以上可以看出地址连续,说明派生类中基类成分和派生类成分地址是连续的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步