这一篇文章主要内容是
派生类构造函数与析构函数
在派生类中重写基类成员
以及多继承
第一部分:派生类构造函数与析构函数
当创建一个派生类对象时,基类成员是如何初始化的?
1.当派生类对象创建的时候,基类成员的初始化顺序
(1)先初始化基类部分,再初始化派生类自己的成员
(2)也就是说,派生类对象在构造时,首先会调用基类的构造函数,把基类的成员变量初始化好,然后才会执行派生类自己的构造函数与成员初始化。
那么一般是什么样子的呢?
class Base {
public:
Base() { cout << "Base构造" << endl; }
~Base() { cout << "Base析构" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived构造" << endl; }
~Derived() { cout << "Derived析构" << endl; }
};
int main() {
Derived d;
return 0;
}
是这样的格式,首先进行的是初始化工作,然后再进行派生。
这里输出的结果是
Base构造
Derived构造
Derived析构
Base析构
第二个问题:当派生类对象销毁时,析构的顺序是什么样子的?
先析构派生类自己的成员,再析构基类部分。
也就是说,派生类对象销毁时,先执行派生类的析构函数,然后再自动调用基类的析构函数。
总结口诀:
构造顺序:先基类,后派生
析构顺序:先派生,后基类
①在创建派生类对象时,首先会调用基类的构造函数,然后再调用派生类的构造函数。
#include<iostream>
class Base{
public:
Base(){
std::cout << "Base constructor called" << std::endl;
}
};
class Derived : public Base{
public:
Derived(){
std::cout << "Derived constructor called" << std::endl;
}
};
int main()
{
Derived d;
return 0;
}
只要创建派生类对象,必然会调用基类构造函数
②当基类的构造函数需要参数时,派生类的构造函数需要显式地调用基类的构造函数并传递参数。
#include<iostream>
using namespace std;
class Base{
public:
Base(int value){
std::cout << "Base constructor called with value" << value << std::endl;
}
};
class Derived : public Base{
public:
Derived(int baseValue,int derivedValue) : Base(baseValue){
std::cout << "Derived construtor called with value" << derivedValue << std::endl;
}
};
int main()
{
Derived d(10,20);
return 0;
}
③派生类构造函数中初始化列表的作用和使用方法。初始化列表可以用于调用
a.基类的构造函数,b.对象成员的构造函数,c.派生类自身的成员变量。
#include <iostream>
using namespace std;
class Base {
public:
Base(int d):data(d) { cout << "Base" << endl; }
int getData() {
return data;
}
protected:
int data;
};
class Derived : public Base {
private:
Base b; //对象成员
int c;
public:
Derived():Base(0),b(1) { cout << "Derived" << endl; }
Derived(int x) :Base(x),b(2*x),c(3*x) { }
};
int main()
{
Derived d(10);
cout << d.getData() << endl;
return 0;
}
④默认构造函数和自定义构造函数。如果基类有默认构造函数,派生类的构造函数可以不显式调用基类的构造函数,编译器会自动调用基类的默认构造函数。但如果基类没有默认构造函数,派生类的构造函数必须显式调用基类的构造函数。
⑤析构函数的调用顺序:在销毁派生类对象时,首先会调用派生类的析构函数,然后再调用基类的析构函数。(一句话:和构造的顺序相反)
#include <iostream>
using namespace std;
class Base {
public:
Base(int d):data(d) { cout << "Base" <<data<< endl; }
int getData() {
return data;
}
~Base() { cout << "Base析构" <<data<< endl; }
protected:
int data;
};
class Derived : public Base {
private:
Base b;//对象成员
int c;
public:
Derived():Base(0),b(1) { cout << "Derived" << endl; }
Derived(int x) :Base(x), b(2 * x) { c = 3 * x; }
~Derived() { cout << "Derived析构" << endl; }
};
int main()
{
Derived d(10);
cout << d.getData() << endl;
return 0;
}
输出结果
-
-
- 在派生类中重写基类成员
-
重写是指在派生类中定义一个与基类中同名、同参数列表、同返回类型的成员函数,以此来改变基类函数的行为。
重载是在同一类中,函数名相同但参数列表不同。
#include<iostream>
using namespace std;
class Base{
public:
void display(){
std::cout << "This is the Base222 class." << std::endl;
}
};
class Derived : public Base{
public:
void display(){
cout << "This is the Base class" << endl;
}
};
int main()
{
Derived d;
d.display();
return 0;
}
重写的函数(派生类中)必须与基类函数的函数名、参数列表和返回类型完全相同。否则就会变成重载(要求参数不同)重写函数的访问权限可以不同,但通常建议保持一致。例如,基类函数是 public,派生类重写函数也用 public。
- 多继承
单继承:一个派生类只能有一个直接基类;当需要组合多个不同类的特性时,单继承无法直接实现,需多继承。即多继承具有更强的代码重用的能力。
class 派生类 : 继承方式 基类1, 继承方式 基类2, ... {
// 成员声明
};
多继承下的构造函数与析构函数的调用顺序
#include<iostream>
using namespace std;
class Speakable{
public:
Speakable() {cout << "Speakable构造" << endl;}
void speak() {cout << "Speaking..." << endl;}
};
class Moveable{
public:
Moveable() {cout << "Moveable构造" << endl;}
void move() {cout << "moving..." << endl;}
};
class Robot : public Speakable,public Moveable{
public:
Robot() : Moveable(),Speakable() {}
};
int main()
{
Robot r;
r.speak();
r.move();
return 0;
}
多继承的二义性:
#include<iostream>
using namespace std;
class A {
public:
void f() { cout << "A"; }
};
class B {
public:
void f() { cout << "B"; }
};
class AB :public A, public B {
public:
//void f() { cout << "AB"; }
};
int main()
{
AB ab;
ab.B::f();
ab.A::f();
return 0;
}
错误提示:对“f”的访问不明确
解决的办法:就是在成员函数的前面加上类名::,说明使用从哪个基类继承的成员函数。
ab.B::f();
ab.A::f();