继承的主要作用:减少代码的数量
class A : public B;
其中A称为子类/派生类,B称为父类/基类
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
继承方式
类中变量的分类
继承方式分为:公有继承、保护继承、私有继承
类中的变量分为:public\protected\private
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A = 1;
void func1()
{
cout<<"m_A:"<<m_A<<endl;
cout<<"m_B:"<<m_B<<endl;
cout<<"m_C:"<<m_C<<endl;
}
protected:
int m_B = 2;
private:
int m_C = 3;
};
int main()
{
Base1 base1;
base1.func1();
cout<<"类外可以访问public变量:"<<base1.m_A<<endl;
cout<<"类外不可以访问protected变量:"<<endl;
cout<<"类外不可以访问private变量:"<<endl;
}
可见:
public成员类内、类外都可以访问。
protected/private成员类内可以访问、类外不可以访问。其两者的主要区别是在继承上。
公有继承
父类public---->子类public
父类protected---->子类protected
父类private---->子类无法访问
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A = 1;
protected:
int m_B = 2;
private:
int m_C = 3;
};
class Son1: public Base1
{
public:
void test_son1()
{
cout<<"父类的public成员:"<<m_A<<endl;
cout<<"父类的protected成员:"<<m_B<<endl;
cout<<"父类的private成员不可以访问:"<<endl;
}
};
int main()
{
Son1 son1;
son1.test_son1();
cout<<"类外可以访问父类的public变量:"<<son1.m_A<<endl;
cout<<"类外不可以访问父类的protected变量:"<<endl;
cout<<"类外不可以访问父类的private变量:"<<endl;
}
保护继承
父类public---->子类protected
父类protected---->子类protected
父类private---->子类无法访问
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A = 1;
protected:
int m_B = 2;
private:
int m_C = 3;
};
class Son1: protected Base1
{
public:
void test_son1()
{
cout<<"父类的public成员:"<<m_A<<endl;
cout<<"父类的protected成员:"<<m_B<<endl;
cout<<"父类的private成员不可以访问:"<<endl;
}
};
int main()
{
Son1 son1;
son1.test_son1();
cout<<"类外不可以父类的访问public变量:"<<endl;
cout<<"类外不可以父类的访问protected变量:"<<endl;
cout<<"类外不可以父类的访问private变量:"<<endl;
}
保护继承
父类public---->子类private
父类protected---->子类private
父类private---->子类无法访问
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A = 1;
protected:
int m_B = 2;
private:
int m_C = 3;
};
class Son1: private Base1
{
public:
void test_son1()
{
cout<<"父类的public成员:"<<m_A<<endl;
cout<<"父类的protected成员:"<<m_B<<endl;
cout<<"父类的private成员不可以访问:"<<endl;
}
};
int main()
{
Son1 son1;
son1.test_son1();
cout<<"类外不可以父类的访问public变量:"<<endl;
cout<<"类外不可以父类的访问protected变量:"<<endl;
cout<<"类外不可以父类的访问private变量:"<<endl;
}
需要注意:父类的private成员也被子类继承过去了,只是编译器给隐藏起来了,访问不到。
父类、子类的构造、析构顺序
父类构造----子类构造----子类析构----父类析构
#include <iostream>
using namespace std;
class Base1
{
public:
Base1(){cout<<"父类构造函数"<<endl;}
~Base1(){cout<<"父类析构函数"<<endl;}
};
class Son1: private Base1
{
public:
Son1(){cout<<"子类构造函数"<<endl;}
~Son1(){cout<<"子类析构函数"<<endl;}
};
int main()
{
Son1 son1;
return 0;
}
同名成员的处理
继承过程中如果出现同名成员,默认调用子类中的成员。如果想调用父类中的成员,需要加上作用域。
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A = 100;
void func1(){
cout<<"父类中的func1函数<<endl;"<<endl;
}
void func1(int value){
cout<<"父类中的func1(value)函数<<endl;"<<endl;
}
};
class Son1: public Base1
{
public:
int m_A = 200;
void func1(){
cout<<"子类中的func1函数<<endl;"<<endl;
}
};
int main()
{
Son1 son1;
// 默认调用子类中的成员
son1.m_A = 300;
cout<<son1.m_A<<endl;
son1.func1();
son1.func1(10); // 错误,父类中的同名成员函数会被隐藏
// 如果想调用父类中的成员的话,需要加上一个作用域
cout<<son1.Base1::m_A<<endl;
son1.Base1::func1();
son1.Base1::func1(10);
}
- 子类对象可以直接访问到子类中同名成员
- 子类对象加作用域可以访问到父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
虚继承
虚继承一般发生在菱形继承的情况下,即:
在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。假如类 A 有一个成员变量 a,那么在类 D 中直接访问 a 就会产生歧义,编译器不知道它究竟来自 A -->B–>D 这条路径,还是来自 A–>C–>D 这条路径。下面是菱形继承的具体实现:
#include <iostream>
#include <string>
using namespace std;
// 间接基类
class Animal
{
public:
string namea = "Animal";
};
// 直接基类
class Sheep: public Animal
{
public:
string nameb = "Sheep";
};
// 直接基类
class Tuo: public Animal
{
public:
string namec = "Tuo";
};
class SheepTuo: public Sheep, public Tuo
{
public:
string named = "SheepTuo";
};
int main()
{
SheepTuo sheeptuo;
// 在两条不同的路径上给namea赋值
sheeptuo.Sheep::namea = "1";
sheeptuo.Tuo::namea = "2";
cout<<sheeptuo.Sheep::namea<<endl;
cout<<sheeptuo.Tuo::namea<<endl;
// 错误,编译器不知道namea是从那条路径获得的
// cout<<sheeptuo.namea<<endl;
}
此时就要用到虚继承:
虚继承:表示一个类做出声明,愿意共享他的基类。这个基类就叫做虚基类。
在上图中,B,C为虚继承,表示愿意共享基类A。其中A称为虚基类。此时派生类D中只会含有一个间接基类A中的元素。
#include <iostream>
#include <string>
using namespace std;
// 间接基类
class Animal
{
public:
string namea = "Animal";
};
// 直接基类
class Sheep: virtual public Animal
{
public:
string nameb = "Sheep";
};
// 直接基类
class Tuo: virtual public Animal
{
public:
string namec = "Tuo";
};
class SheepTuo: public Sheep, public Tuo
{
public:
string named = "SheepTuo";
};
int main()
{
SheepTuo sheeptuo;
// 此时两条路径上的输入会同时影响namea
// 因为此时namea被两个类共享
sheeptuo.Sheep::namea = "1";
sheeptuo.Tuo::namea = "2";
// 次数均为2
cout<<sheeptuo.Sheep::namea<<endl;
cout<<sheeptuo.Tuo::namea<<endl;
// 不会报错,此时sheeptuo中只包含一个namea
cout<<sheeptuo.namea<<endl;
}
虚继承的关键在于共享。虚继承的两个类愿意共享基类中的成员变量