一、概念
继承是类设计层次复用的核心手段。通过继承,子类可以复用基类的成员,同时扩展新功能。
语法示例:
class Person { /* 基类 */ };
class Student : public Person { /* 派生类 */ };
继承关系与访问限定符紧密相关。继承后,基类成员的可见性会发生变化:
- 私有成员:虽然被继承到了派生类对象中,但语法上限制派生类(无论内部还是外部)都无法直接访问。它们在基类内可用,在基类外不可见。
- 保护成员:如果希望基类成员不在类外直接访问,但在派生类中可访问,应定义为
protected。 - 访问权限计算:基类其他成员在子类的访问方式遵循规则:Min(成员在基类的访问限定符,继承方式)。即
public > protected > private。
二、基类和派生类对象赋值转换
不同类型对象赋值需要类型转换,但继承体系中有特殊规则。
父类对象不能赋值给子类对象,强转也不行。否则子类特有的成员变量怎么办?给随机值吗?
Person p; Student s;
s = p; // 错误:父类不能赋值给子类
// s = (Student)p; // 强转也不行
p = s; // 正确
子类对象可以赋值给父类的对象、指针、引用。 当子类赋值给父类对象时,语法上特殊处理,没有发生类型转换,而是赋值兼容(切片)。把子类里属于父类的那部分切出来,拷贝给父类。
怎么证明没有发生类型转换?可以用别名验证。如果把父类的成员变量放成 public,可以看到 p2 变成了子类中父类那一部分的别名,而非整个子类对象。
三、继承中的作用域
作用域查找是编译时的规则。定义了一个类就有类域,子类和父类有独立的类域。
同名成员隐藏:子类成员将屏蔽父类对同名成员的直接访问,这叫隐藏(重定义)。在子类成员函数中,可以使用 父类::父类成员 显式访问。
现实中尽量不要在继承体系里定义同名成员,这容易造成混淆。
class Person {
public:
void fun() { cout << "Person::func()" << endl; }
protected:
string _name = "小李子";
int _num = 111;
};
class Student : Person {
:
{ cout << << endl; }
{
cout << << _name << endl;
cout << _num << endl;
cout << Person::_num << endl;
}
:
_num = ;
};
{
Student s;
s.();
s.();
s.Person::();
;
}


