1、C++的三种权限管控
class A
{
public:
//成员变量和函数,在类的其他成员函数和类的对象可以直接访问
private:
//成员变量和函数,在类的其他成员函数中可以直接访问,但是不可以通过类的对象直接访问,在派生类中不可以直接访问
protected:
//成员变量和函数,在类的其他成员函数中可以直接访问,但是不可以通过类的对象直接访问,在派生类中可以直接访问
};
2、C++为什么要设计访问权限
(1)保护内部资源。比如:private的成员是class内部使用,外部没必要访问或者说不想外部能直接访问,于是语法上就将需要保护的资源权限定义为private,于是外部便不能直接访问;
(2)隐藏外部无需关心的细节。比如:外部能直接访问的就是public权限的变量和函数,至于public的成员方法去访问private和protected权限的变量和函数,外部便不需要感知;
(3)隐藏class中不需要外部关注的细节,可以降低使用class类的难度;
(4)隐藏不需要外部关注的细节,也可以防止外部不小心去修改;
(5)访问权限是语法层面的保护,是编译器在编译链接时帮我们检查权限,而不是硬件内存上存在这些权限;
3、设计类时如何规定成员的访问权限
(1)需要被外界访问的成员直接设置为public
(2)只能在当前类中访问的成员设置为private
(3)只能在当前类和子类中访问的成员设置为protected。
4、C语言中的"继承"
//基类
struct A
{
int a;
}
//派生类
struct B
{
struct A; //继承struct A
int b;
}
(1)首先C语言没有继承这个机制和概念,但是实际代码中却有类似继承的用法;
(2)上面的struct A可以看做是基类,struct B是派生类继承了struct A,这种用法和C++的继承思想是一样的;
5、C++中的继承
5.1、继承特性的语法
class 派生类名:访问控制 基类名1,访问控制 基类名2,访问控制 基类名n
{
// 成员变量和成员方法列表(除了构造和析构之外的成员方法)
};
(1)新构建类时,可以同时继承多个基类,基类之间以逗号分隔;
(2)访问控制:指明继承的方式,分为public(共有)继承、private(私有)继承、protected(保护)继承;
5.2、继承的概念
(1)继承是C++源生支持的一种语法特性,是C++面向对象的一种表现;
(2)继承特性可以让派生类“瞬间”拥有基类的所有(当然还得考虑权限)属性和方法;
(3)继承特性本质上是为了代码复用;
5.3、派生类和基类的关系
(1)派生类,表示范围小,更具体;基类,表示范围大,更抽象。比如:人这个概念范围就很大,男人和女人表示的范围比人就要小一些,男人和女人的概念更具体,但是男人和女人都属于人,所以男人和女人这两个类都可以去集成人这个基类;
(2)派生类和基类是从属关系,可以描述为:派生类是基类的一种。比如:男人是人的一种,而不能说人是男人的一种;
(3)派生类集成基类只是为了借助基类快速定义派生类,派生类定义好后,派生类的对象访问的都是派生类自己的成员变量和成员函数,和基类已经没有关系了;
5.4、三种继承的结果
基类成员 | 继承后的权限 |
---|---|
基类的public | 经过public继承,在子类中是public;经过private继承,在子类中是private;经过protected继承,在子类中是protected; |
基类的private | 不管何种继承方式,基类的private成员在派生类中都是一种比private管控更严格的权限。换句话说,就是派生类是不能直接访问基类的private成员的; |
基类的protected | 经过public继承,在子类中是protected;经过private继承,在子类中是private;经过protected继承,在子类中是protected; |
5.5、为什么基类的private成员,在派生类中不能直接访问?
(1)首先派生类是继承了基类的所有成员,包括基类的private成员;
(2)private成员表示这些成员是类私有的,不希望外部能访问。
(3)基类的private成员,表示这些成员是基类私有的,外部不能访问;虽然派生类继承了基类,但是派生类并不是基类,如果派生类也能直接访问基类的private成员,那就打破了private的定义;
(4)派生类只是不能直接访问基类的private成员,但是可以通过基类提供的public、protected权限的成员去间接访问;
6、三种继承方式的示例代码
6.1、public继承
#include <iostream>
#include <string>
using namespace std;
//person类表示所有人共有的特性
class person
{
public:
int gender; //性别,0代表男,1代表女
void getName(void);
//构造函数
person(){};
person(string myName, int myGender, int myAge):name(myName),gender(myGender),age(myAge){}
private:
string name;
protected:
int age;
};
void person::getName(void)
{
cout << "person::getName name=" << this->name << endl;
}
//adult类表示成年人
class adult:public person
{
public:
void work(void); //成年人多增加工作的特性
//构造函数
adult(){};
adult(string myname, int myGender, int myAge);
void print(void);
private:
protected:
};
//adult类的构造函数
adult::adult(string myname, int myGender, int myAge):person(myname, myGender, myAge)
{
}
void adult::work(void)
{
cout << "man::work" << endl;
}
void adult::print(void)
{
//cout << "man::name=" << this->name << endl;
cout << "man::gender=" << this->gender << endl;
cout << "man::age=" << this->age << endl;
}
int main(void)
{
adult ad("linux", 1, 23);
ad.getName();
ad.print();
//cout << "ad.name=" << ad.name << endl;
cout << "ad.gender=" << ad.gender << endl;
//cout << "ad.age=" << ad.age << endl;
return 0;
}
6.2、private继承
#include <iostream>
#include <string>
using namespace std;
//person类表示所有人共有的特性
class person
{
public:
int gender; //性别,0代表男,1代表女
void getName(void);
//构造函数
person(){};
person(string myName, int myGender, int myAge):name(myName),gender(myGender),age(myAge){}
private:
string name;
protected:
int age;
};
void person::getName(void)
{
cout << "person::getName name=" << this->name << endl;
}
//adult类表示成年人
class adult:private person
{
public:
void work(void); //成年人多增加工作的特性
//构造函数
adult(){};
adult(string myname, int myGender, int myAge);
void print(void);
private:
protected:
};
//adult类的构造函数
adult::adult(string myname, int myGender, int myAge):person(myname, myGender, myAge)
{
}
void adult::work(void)
{
cout << "man::work" << endl;
}
void adult::print(void)
{
//cout << "man::name=" << this->name << endl;
cout << "man::gender=" << this->gender << endl;
cout << "man::age=" << this->age << endl;
}
int main(void)
{
adult ad("linux", 1, 23);
//ad.getName();
ad.print();
//cout << "ad.name=" << ad.name << endl;
//cout << "ad.gender=" << ad.gender << endl;
//cout << "ad.age=" << ad.age << endl;
return 0;
}
6.3、protected继承
#include <iostream>
#include <string>
using namespace std;
//person类表示所有人共有的特性
class person
{
public:
int gender; //性别,0代表男,1代表女
void getName(void);
//构造函数
person(){};
person(string myName, int myGender, int myAge):name(myName),gender(myGender),age(myAge){}
private:
string name;
protected:
int age;
};
void person::getName(void)
{
cout << "person::getName name=" << this->name << endl;
}
//adult类表示成年人
class adult:protected person
{
public:
void work(void); //成年人多增加工作的特性
//构造函数
adult(){};
adult(string myname, int myGender, int myAge);
void print(void);
private:
protected:
};
//adult类的构造函数
adult::adult(string myname, int myGender, int myAge):person(myname, myGender, myAge)
{
}
void adult::work(void)
{
cout << "man::work" << endl;
}
void adult::print(void)
{
//cout << "man::name=" << this->name << endl;
cout << "man::gender=" << this->gender << endl;
cout << "man::age=" << this->age << endl;
}
int main(void)
{
adult ad("linux", 1, 23);
//ad.getName();
ad.print();
//cout << "ad.name=" << ad.name << endl;
//cout << "ad.gender=" << ad.gender << endl;
//cout << "ad.age=" << ad.age << endl;
return 0;
}
6.4、示例代码分析
(1)屏蔽起来的代码就是表示不能访问的,否则就会报错,和三种继承的权限结合起来看;
(2)可以将权限的严格程序做个排序:private > protected > public;
(3)继承时候确认基类成员在派生类中的权限,可以理解成:比较该成员在基类中的权限和继承权限,选择最高的那个权限作为基类成员在派生类中的权限;
7、不良继承
7.1、何为不良继承
(1)鸵鸟不是鸟的问题:鸟作为基类提供了fly的方法,鸵鸟属于鸟,所以去集成了鸟这个基类,但是鸵鸟并不会fly,从基类继承的fly这个方法对鸵鸟来说就是很多余的;
(2)不良继承是客观存在的。比如上面的"鸵鸟不是鸟"的问题,你不把鸵鸟划分成鸟不恰当,你把鸵鸟划分为鸟也不太恰当,毕竟鸵鸟除了不会飞其他都是满足鸟的特性;
7.2、解决办法
(1)消除继承关系:既然鸵鸟继承鸟会出现不会fly这种不良继承,那就尝试找一个更贴近的基类去继承;
(2)弱化基类:将鸟这个基类中fly成员去掉,各种鸟去继承基类时,根据自己的情况决定是否添加fly这个成员;
总结:两种方式没有高低之分,根据实际情况来采用;
8、派生类和基类的构造/析构函数关系
9、派生类和基类的同名成员问题
(1)在派生类和基类中存在同名的成员时,派生类的对象调用的是派生类实现的成员,此时基类的成员被隐藏,也叫重定义;
(2)但是在派生类还是可以调用基类的同名成员的,可以使用指定域名的方法:父类::成员;显示的指定要调用父类的成员而不是派生类的成员;
10、派生类和基类的类型兼容规则
10.1、派生类和基类的关系
(1)C++语言是强类型语言,每个变量都是有类型的,如果类型不匹配编译就会报错;
(2)派生类是基类的超集:派生类继承基类,所以基类有的派生类全都有,而派生类独有的基类则没有;
(3)派生类的对象可以裁剪后当基类使用,而基类的对象则不能放大当派生类使用,否则可能会出错;
总结:派生类对象可以当基类的对象使用,反之则不行;
10.2、类型兼容规则
(1)子类对象可以当作父类对象使用,也就是说子类对象可以无条件隐式类型转换为一个父类对象
(2)子类对象可以直接初始化或直接赋值给父类对象
(3)父类指针可以直接指向子类对象
(4)父类引用可以直接引用子类对象