目录
1.继承的基础语法
语法: class 子类名 :继承方式 父类名
子类也称派生类
父类也称基类
class Base
{
public:
Base()
{
m_A=10;
}
int m_A;
};
class son :public Base//继承的基础语法
{
public:
son()
{
int m_B=20;
}
};
//完成继承后,son就拥有了两个属性。
void test()
{
son s;
cout<<s.m_A;//子类的对象可以调用父类中的属性
cout<<s.m_B;
}
2.继承方式
继承的方式分为3种:(所有继承方式中,子类都可以继承父类中的所有属性,只是访问权限不同和继承之后内容权限的改变不同)
私有成员只是被隐藏了,但还是会继承下去。
-
公共继承:子类继承父类中的所有属性,不能访问继承来的私有内容。继承到子类中的内容,按父类原有的属性权限(原来是公共继承后还是公共,保护、私有同理)。
-
保护继承:子类继承父类中的所有属性,不能访问继承来的私有内容。继承到子类的内容中,原来父类中的公共内容到子类中变为保护内容。
-
私有继承:子类继承父类中的所有属性,不能访问继承来的私有内容。继承到子类的内容中,原来父类中的公共内容和保护内容到子类中都变为私有内容。
class Base //父类
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class son1 :public Base //以公共继承方式继承
{
public:
void func()
{
m_A = 100;
m_B = 200;
//m_C = 300;//在子类中,成员属性的权限和父类相同,但子类不可访问父类的私有属性
}
};
class son2 : protected Base
{
public:
void func()
{
m_A = 100;//以保护方式继承来的属性m_A,其原来权限为公开,现在变为保护。
m_B = 200;
//m_C = 300;
}
};
class son3 :private Base
{
public:
void func()
{
m_A = 100;//以私有方式继承来的属性m_A,其原来权限为公开,现在变为私有。
m_B = 200; //同上,保护变为私有。
//m_C = 300; //在子类中都为私有属性,在类内可以访问,当不可以访问继承来的私有属性。
}
};
void test01()
{
son1 s;
s.m_A = 100;
//s.m_B = 200;//保护内容不可以在函数外访问。
//s.m_C = 300;//私有内容不可以在函数外访问。
cout << s.m_A << endl;
}
void test02()
{
son2 s;
//s.m_A = 100;//保护内容不可以在函数外访问。
//s.m_B = 200;//保护内容不可以在函数外访问。
//s.m_C = 300;//私有内容不可以在函数外访问。
//cout << s.m_A << endl;
}
void test03()
{
son3 s;
//s.m_A = 100;//私有内容不可以在函数外访问。
//s.m_B = 200;//私有内容不可以在函数外访问。
//s.m_C = 300;//私有内容不可以在函数外访问。
//cout << s.m_A << endl;
}
3.继承中的结构和析构的关系
-
先执行父类构造函数再执行子类构造函数
-
先执行子类析构函数再执行父类析构函数
class Base
{
public:
Base()
{
cout << "Base中的结构造数调用" << endl;
}
~Base()
{
cout << "Base中的析构函数调用" << endl;
}
};
class son:public Base
{
public:
son()
{
cout << "son中的构造函数调用" << endl;
}
~son()
{
cout << "son中的析构函数调用" << endl;
}
};
void test01()
{
son s;
}
输出:
Base中构造函数调用
Son中构造函数调用
Son中析构函数调用
Base中析构函数调用
4.继承中同名成员的处理方式
-
访问子类的同名成员时,可以直接在类内访问,或者在类外通过对象直接访问。
-
访问父类成员时,需要再子类访问的基础上加上作用域。
-
如果子类中出现和父类同名的成员函数,子类的同名函数会隐藏掉父类中的所有同名成员函数不管有没有带参数。
class Base
{
public:
Base()
{
m_A = 100;
}
int m_A;
int m_B;
void func()
{
cout << "Base中函数的调用" << endl;
}
void func(int a )
{
cout << "Base中的带参数函数调用" << endl;
}
};
class Son:public Base
{
public:
Son()
{
m_A = 200;
}
int m_A;
int m_B;
void func()
{
cout << "son中函数的调用" << endl;
}
};
void test01()
{
Son s1;
cout<<s1.m_A<<endl;
cout<<s1.Base::m_A << endl; //访问父类中同名函数,需加上作用区。
}
void test02()
{
Son s2;
s2.func();
//s2.func(10);如果子类中出现和父类同名的成员函数,子类的同名函数会隐藏掉父类中的所有同名成员函数不管有没有带参数。
s2.Base::func();
s2.Base::func(10);
}
5.继承同名静态成员处理方式
-
静态成员和非静态成员出现同名,处理方式一致(加作用域)。
-
static :所有对象共享同一份数据,编译阶段分配内存,类内声明,类外初始化。
-
静态成员函数只能访问静态成员函数,所有对象也都共享同份数据。由于共享同一份数据所以静态成员有两种访问方式。
-
通过对象访问数据
-
通过类名访问数据
-
//只演示静态变量的调用,函数和变量同理。
class Base //父类
{
public:
static int m_A;//静态变量,类内声明
};
int Base::m_A = 100;//静态变量,类外初始化
class Son:public Base //子类继承父类
{
public:
static int m_A;
};
int Son::m_A = 200;//类外初始化
void test01()
{
//通过对象访问数据
Son s1;
cout << s1.m_A << endl;
cout << s1.Base::m_A << endl;
//通过类名访问数据
cout << Son::m_A << endl;
cout << Son::Base::m_A << endl;//第一个::代表通过类名方式访问 第二个::代表访问父类作用域下
}
6.多继承语法
语法 : class 子类:继承方式 父类1,继承方式 父类2,.......
- 使用过程中,可能由于父类过多,导致出现属性名称相同,导致二异性。需要通过加作用域区分
class Base1 //父类1
{
public:
int m_A;
};
class Base2 //父类2
{
public:
int m_A;
};
class son :public Base1, public Base2 //子类以公开的继承方式,继承多个父类
{
public:
son()
{
Base1::m_A = 1;//使用中需要加作用域加以区分
Base2::m_A = 2;
}
};
void test01()
{
son s
//cout<<s.m_A<<endl;//出现二异性,不知道m_A是来自那个父类
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
7.菱形继承
概念:两个子类(B&C)继承同一个父类(A),又有某个类(D)同时继承两个子类。
B继承了A的数据,C继承了A的数据,当D使用数据是就会产生二义性。
D继承了两份数据,造成资源浪费。
此时我们可以利用虚继承来解决菱形继承出现的问题。
继承之前加上关键字virtual变为虚继承
A类成为虚基类,继承虚基类,实质是继承了一个指向基类属性的指针
//动物类
class Animal
{
public:
int m_Age;
};
//羊类
class Sheep : virtual public Animal {}; //加上virtual,继承的是一个指向Animal属性的指针
//骆驼类
class Tuo :virtual public Animal {};
//羊驼类
class SheepTuo :public Sheep, public Tuo {};
void test02()
{
SheepTuo st2;
st2.Sheep::m_Age = 18;
st2.Tuo::m_Age = 28;
//可以正常打印输出,但是我们只需要一个数据
//加上virtual关键字后,数据唯一。当修改成28后Sheep中的m_Age也为28;
cout << st2.Sheep::m_Age << endl; //输出28
cout<< st2.Tuo::m_Age <<endl; //输出28,因为数据唯一
}