1 派生类对象模型
1.1 派生类对象的组成部分
派生类对象包含多个组成部分,包括:
(1)派生类自己定义的子对象(成员变量、成员函数)。
(2)派生类从基类继承得到的子对象(基类中定义的成员变量、成员函数)。
1.2 基类指针与派生类指针
由于子类对象中包含父类的对象,因此,子类的指针范围大于父类指针范围。所以,可以用父类指针指向子类对象,而子类指针无法指向父类对象。
#include <iostream>
using namespace std;
class Animal
{
};
class Dog : public Animal
{
};
int main() {
Animal *animal1 = new Dog; //父类指针可以指向子类对象,此时编译器帮我们隐式的做了子类到父类的转换
// Dog *dog = new Animal; //错误, 子类指针不可以指向父类对象。编译器无法完成父类到子类的转换
}
2 派生类构造函数
(1)派生类其实使用基类的构造函数来初始化基类部分的成员,即,基类控制基类部分的初始化,派生类控制派生类部分的初始化。
(2)在创建子类对象时,先调用基类构造函数,再调用派生类构造函数。
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal类构造函数" << endl;
}
};
class Dog : public Animal
{
public:
Dog()
{
cout << "Dog类构造函数" << endl;
}
};
int main() {
Dog *dog = new Dog; //Animal类构造函数, Dog类构造函数
}
(3)通过派生类构造函数给基类构造函数传值
#include <iostream>
using namespace std;
class Animal
{
public:
Animal(int age) : m_Age(age) //初始化列表
{
cout << "Animal的年龄是" << m_Age << endl;
}
int m_Age;
};
class Dog : public Animal
{
public:
Dog(int age1, int age2) : Animal(age1), m_Age(age2) //将age1传递给父类Animal的构造函数,将age2传递给自己的构造函数
{
cout << "Dog的年龄是" << m_Age << endl;
}
int m_Age;
};
int main() {
Dog(10, 20); //Animal的年龄是10, Dog的年龄是20
}
3 继承关系的传递性
继承具有传递性,B是A的子类,C是B的子类,则C继承了A和B的所有成员。
#include <iostream>
using namespace std;
class Animal
{
public:
int m_Age;
Animal(int age) : m_Age(age){}
};
class Dog : public Animal
{
public:
string m_Name;
Dog(int age, string name) : Animal(age), m_Name(name){}
};
class Erha : public Dog
{
public:
Erha(int age, string name) : Dog(age, name){} //Dog类构造函数需要m_Age,和m_Name来进行初始化
};
int main() {
Erha erha(10, "shiyi");
cout << erha.m_Name << " " << erha.m_Age;
}
4 不成为基类
在某个类后面加上final关键字,则该类不允许其他类继承
class Animal final
{
};
class Dog : public Animal //错误, Animal类不允许被继承
{
};
5 静态类型与动态类型
(1)静态类型是指变量声明时的类型,在编译时已经确定。
(2)动态类型是指基类指针/引用所指内存中对象的类型,在运行时才能确定。
(3)只有基类指针/引用才会出现静态类型和动态类型不一致的问题。(基类指针/引用指向子类对象)。
6 父类与子类间的拷贝与赋值
(1)用子类对象初始化父类对象时,会自动调用父类的拷贝构造函数。
#include <iostream>
using namespace std;
class Animal
{
public:
int m_Animal;
Animal(int i) : m_Animal(i){} //使用拷贝构造函数时,系统不再自动为我们定义构造函数,需手动定义
Animal(const Animal& tmpDog) //tmpDog得到了Dog类对象的引用,但是其只能处理自己的成员不能处理子类成员
{
m_Animal = tmpDog.m_Animal; // 自己定义拷贝构造函数时,系统不再逐一拷贝,须手动拷贝成员
cout << "执行Animal拷贝构造函数" << endl; // 10
// cout << m_Dog << endl; //错误,不可以处理子类成员
cout << m_Animal << endl;
}
};
class Dog : public Animal
{
public:
int m_Dog;
Dog(int i, int j) : Animal(i), m_Dog(j){}
};
int main() {
Dog dog(10, 20);
Animal animal(dog); //执行Animal拷贝构造函数
}
(2)子类对象赋值给父类对象时,会自动调用赋值运算符
#include <iostream>
using namespace std;
class Animal
{
public:
Animal& operator=(const Animal& tmpDog)
{
cout << "调用赋值运算符" << endl; //调用赋值运算符
return *this;
}
};
class Dog : public Animal
{
};
int main() {
Animal animal;
Dog dog;
animal = dog; //子类对象赋值给父类对象,自动调用赋值运算符
}
结论:
用派生类对象给基类对象赋值/拷贝时,只有该派生类对象的基类部分参与拷贝或赋值,其余部分不参与。