面向对象语言最重要的目的之一就是减少代码的复用,为了这一目标的实现,我们需要抽象出事物的本质或者说属性,而继承便是在这之上而诞生的。
继承机制是面向对象程序设计使代码可以复用的最重要的手段之一,它允许程序员在保持原有类特性(内容)的基础上进行进一步的拓展,而在这之上产生的类就是派生类。
class person
{
public:
person() { cout << "person()" << endl; };
void print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
};
protected:
string _name = "Alice";
int _age = 18;
};
//父类的所有成员都会被子类所继承,如果成员函数同名同参同返回则构成重定义。
class Student : public person
{
protected:
int _id = 12345;
};
int main() {
Student stu;
person per;
stu->print();
per->print();
}
name:Alice
age:18
name:Alice
age:18
我们可以从结果看到子类不仅继承了父类的成员变量,也继承了父类的成员函数。
继承有三种方式去继承,分别是公有继承、保护继承、私有继承。
类成员 | public继承 | protected继承 | private继承 |
---|---|---|---|
基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
基类的protected | 派生类的protected成员 | 派生类的private成员 | 派生类的private成员 |
基类的private成员 | 不可见 | 不可见 | 不可见 |
总结:
1. 基类private成员无论以何种方式继承都是不可见的,当然也无法被访问。
2. 如果不显示定义继承方式,则class的默认继承方式为private,struct的则为public。
3. 不难看出继承里面权限最大的为public继承,也就是说 public > protected > private。
派生类的对象可以不用通过创造临时对象来间接赋值给基类,而直接赋值给基类的对象。我们称这种情况为“切片”。
注意:基类对象不能赋值给派生类对象。
基类的指针/引用可以用子类的来代替,但必须是基类的指针/引用指向子类对象时才是安全的。同时这也是多态的形成方式。
class person
{};
class student
{};
//子类对象可以直接赋值给父类的指针/引用
person* ptr = new student;
student stu;
person& p1 = stu;
//基类指针可以通过强制类型转换赋值给派生类的指针
person pp;
student* p2 = (student*)pp; //可能会存在越界访问的问题
继承体系中基类与父类有着不同的作用域,如果子类与父类有同名的成员,那么子类的成员会覆盖掉父类的同名成员,这也叫做重定义或隐藏。
class A
{
public:
void func1() { cout << "A::_val=" << _val << endl; }
private:
int _val = 1;
};
class B : public A
{
public:
//成员函数要构成隐藏/重定义必须要同名
void func1() { cout << "B::val=" << _val << endl; }
private:
int _val = 0;
};
B b1;
b1.func1();
B::val=0
在继承体系中,基类的构造函数必须是第一个被调用的,而基类的析构函数则必须是最后一个调用。我们能从下面的代码看出:
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
};
class B : public A
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
};
B bb;
注意:友元与静态成员不能被继承。定义了一个静态成员,那么之后无论派生多少子类都只有一个静态成员。
c++继承体系分为单继承与多继承, 其中菱形继承是多继承的一种特殊情况。
单继承:
多继承:
菱形继承:
因为菱形继承子类继承了多份父类的数据,所以会导致数据存在冗余与二义性问题。
如果需要访问基类的成员,必须使用访问限定符来限定访问的对象,基于种种原因日常使用应该避免使用菱形继承。
D dd;
dd.B::_val = 0;
dd.A::_val = 1;
在C++里面,菱形继承会造成二义性和数据冗余如果要解决这种问题必须要使用虚继承,不过这就不属于文的部分了。
博客主页:主页
我的专栏:C++
我的github:github