C++兼容性规则
一个公有派生类的对象在使用上可以被当作基类的对象,反之则禁止。具体表现在:
- 派生类的对象可以被赋值给基类对象。
- 派生类的对象可以初始化基类的引用。
- 指向基类的指针也可以指向派生类。
通过基类对象名、指针只能使用从基类继承的成员
举例:
1.派生类的对象可以被赋值给基类对象。这是一个总纲领,意味着在需要基类对象的地方,我们都可以用一个派生类对象来替代。
这会导致对象切片
#include <iostream>
#include <string>// 基类
class Person {
public:std::string name;void introduce() {std::cout << "我是一个人, 我的名字是 " << name << std::endl;}
};// 公有派生类
class Student : public Person {
public:int studentID; // 派生类特有的成员void study() { // 派生类特有的方法std::cout << name << " 正在学习, 学号是 " << studentID << std::endl;}
};int main() {Student stu;stu.name = "张三";stu.studentID = 101;Person per;per = stu; // 将派生类对象 stu 赋值给 基类对象 perstd::cout << "基类对象 per 的名字: " << per.name << std::endl;per.introduce();// per.studentID = 102; // 错误! per 是一个 Person 对象, 它没有 studentID 成员。// per.study(); // 错误! per 是一个 Person 对象, 它没有 study() 方法。
}
解释:
当执行 per = stu; 时,stu 对象中从 Person 继承来的部分(也就是 name 成员)被拷贝到了 per 对象中。stu 对象自己独有的成员(studentID)被完全“切掉”和丢弃了。因此,per 仍然是一个纯粹的 Person 对象,它只知道 name,不知道任何关于 studentID 或 study() 的信息。
2.派生类的对象可以初始化基类的引用。这是实现多态的一种非常安全和常见的方式,不会发生对象切片。
#include <iostream>
#include <string>// 基类
class Person {
public:std::string name;void introduce() {std::cout << "我是一个人, 我的名字是 " << name << std::endl;}
};// 公有派生类
class Student : public Person {
public:int studentID; // 派生类特有的成员void study() { // 派生类特有的方法std::cout << name << " 正在学习, 学号是 " << studentID << std::endl;}
};int main() {Student stu;stu.name = "李四";stu.studentID = 102;// 使用派生类对象 stu 来初始化一个基类的引用Person& per_ref = stu;std::cout << "通过基类引用访问名字: " << per_ref.name << std::endl;per_ref.introduce(); // 调用的是 Person 的方法// per_ref.study(); // 错误! 虽然引用指向的是 Student 对象,但引用本身是 Person 类型,// 只能访问 Person 中定义的成员。
}
解释:
per_ref 是基类 Person 的一个引用,它直接绑定到了 stu 这个派生类对象上。内存中只有一个 stu 对象,没有发生任何拷贝。per_ref 成为了 stu 对象的一个“别名”,但这个别名是 Person 类型的,所以它有一个受限的“视野”。
3.指向基类的指针也可以指向派生类。这是实现多态最核心、最灵活的方式,同样不会发生对象切片。
#include <iostream>
#include <string>// 基类
class Person {
public:std::string name;void introduce() {std::cout << "我是一个人, 我的名字是 " << name << std::endl;}
};// 公有派生类
class Student : public Person {
public:int studentID; // 派生类特有的成员void study() { // 派生类特有的方法std::cout << name << " 正在学习, 学号是 " << studentID << std::endl;}
};int main() {Student stu;stu.name = "王五";stu.studentID = 103;// 基类指针指向派生类对象Person* per_ptr = &stu;std::cout << "通过基类指针访问名字: " << per_ptr->name << std::endl;per_ptr->introduce(); // 调用的是 Person 的方法// per_ptr->study(); // 错误! 指针类型是 Person*,它“看”不到 Student 类中新增的成员。
}
解释:
`per_ptr` 是一个 `Person` 类型的指针,它存储了 `stu` 对象的内存地址。和引用一样,它也只是提供了一个基类“视角”来观察这个派生类对象。