C++经验之类继承

版权声明:本文为博主原创文章,转载时请标明来源。 https://blog.csdn.net/aiwangtingyun/article/details/86567256

成员访问控制

在C++中,类的成员访问控制分为公有public,保护protected,私有private。其访问权限对象分为类成员函数和用户(类的实例化或对象),派生类后面讨论。不管是公有,保护还是私有,自身类的成员函数都可以直接访问(“自身类”是为了区分后面的派生类)。但是用户(类的对象)只能访问公有成员,保护成员和私有成员都不能访问。

比如:

// 定义一个类
class CMyClass
 {
	// 没问题,可以访问公有成员
	int GetMem() { return m_public; }
	
	// 没问题,可以访问保护成员
	int GetMem() { return m_protect; }
	
	// 没问题,可以访问私有成员
	int GetMem() { return m_private; }
	
public:
	int m_public;					// 公有成员变量
protected:
	int m_protect;					// 保护成员变量
private:
	int m_private;					// 私有成员变量
};

// 实例化类的对象
CMyClass obj;

obj.m_public = 1;					// 没问题,用户可以访问公有成员
obj.m_protect = 1;					// 错误,用户不可以访问保护成员
obj.m_private = 1;					// 错误,用户不可以访问私有成员

继承分类

现在来讨论一下派生类的派生访问说明符,同样包括公有继承public,保护继承protected和私有继承private。这些派生访问说明符对派生类本身没有任何影响,无论是哪种继承派生类本身都只能访问基类的公有成员和保护成员,不能访问基类的私有成员。这些派生访问说明符是用于控制派生类用户(派生类对象)对基类成员的访问权限。

比如:

// 定义一个基类
class Base
 {
public:
	int m_public;					// 公有成员变量
protected:
	int m_protect;					// 保护成员变量
private:
	int m_private;					// 私有成员变量
};

// 公有继承派生类
class Derived_Public : public Base
 {
	// 没问题,可以访问基类公有成员 
	int GetPublic() { return m_public; }
	
	// 没问题,可以访问基类保护成员 
	int GetProtect() { return m_protect; }
	
	// 错误,不可以访问基类私有成员 
	int GetPrivate() { return m_private; }
};

// 保护继承派生类
class Derived_Protect : protected Base
 {
	// 没问题,可以访问基类公有成员 
	int GetPublic() { return m_public; }
	
	// 没问题,可以访问基类保护成员 
	int GetProtect() { return m_protect; }
	
	// 错误,不可以访问基类私有成员 
	int GetPrivate() { return m_private; }
};

// 私有继承派生类
class Derived_Private : private Base
 {
	// 没问题,可以访问基类公有成员 
	int GetPublic() { return m_public; }
	
	// 没问题,可以访问基类保护成员 
	int GetProtect() { return m_protect; }
	
	// 错误,不可以访问基类私有成员 
	int GetPrivate() { return m_private; }
};

从上面可以看到无论是哪种方式继承,派生类类本身都是只能访问基类的公有和保护成员,不能访问私有成员。下面看一下派生访问说明怎么限制用户(派生类的对象):

// 定义一个公有继承的派生类对象
Derived_Public pub;

// 没问题,公有继承的派生类对象可以访问基类的公有成员
pub.m_public = 1;

// 错误,公有继承的派生类对象不可以访问基类的保护成员
pub.m_protect = 1;

// 错误,公有继承的派生类对象不可以访问基类的私有成员
pub.m_private = 1;
// 定义一个保护继承的派生类对象
Derived_Protect prot;

// 错误,保护继承的派生类对象不可以访问基类的公有成员
prot.m_public = 1;

// 错误,保护继承的派生类对象不可以访问基类的保护成员
prot.m_protect = 1;

// 错误,保护继承的派生类对象不可以访问基类的私有成员
prot.m_private = 1;
// 定义一个私有继承的派生类对象
Derived_Protect priv;

// 错误,私有继承的派生类对象不可以访问基类的公有成员
priv.m_public = 1;

// 错误,私有继承的派生类对象不可以访问基类的保护成员
priv.m_protect = 1;

// 错误,私有继承的派生类对象不可以访问基类的私有成员
priv.m_private = 1;

现在,我们应该可以分清派生访问说明符的作用了吧!公有继承过来的基类成员对于派生类对象访问限制不变,在基类里是公有的、保护的、私有的在派生类里还是保持公有、保护、私有。所以公有继承的派生类对象可以访问基类的公有成员但却不能访问基类的保护和私有成员。保护继承过来的基类所有成员对于派生类对象来说访问权限变成了保护,而对象是不能访问类的保护成员的,所以保护继承的派生类对象不能访问基类的任何成员。同样的道理,私有继承过来的基类成员对于派生类对象来说访问权限是私有,所以其对应的对象不能访问基类的任何成员。


虚继承

在多重继承体系下(派生类继承多个基类),往往会出现基类中又有更高级基类的情况,考虑下面这种继承情况:

class File { private: string fileName; };
class InputFile: public File { ... };
class OutputFile: public File { ... };
class IOFile: public InputFile, public OutputFile { ... };

将其转换为继承模型图为:
在这里插入图片描述
这种情况下派生类IOFile和基类File之间的相通路线有一条以上,这就是所谓的钻石型或棱形继承,最要命的是,IOFile类通过 IOFile -> InputFile -> File 这条继承路线已经继承了一份fileName成员变量,而通过 IOFile -> OutputFile -> File 这条继承路线又继承了一份fileName成员变量,也就是说成员变量fileName执行了两次复制。

解决以上问题的关键是使用虚继承,令带有此数据(fileName)的类(File)成为虚基类(virtual base class)。所以将继承体系改写成如下:

class File { private: string fileName; };
class InputFile: virtual public File { ... };
class OutputFile: virtual public File { ... };
class IOFile: public InputFile, public OutputFile { ... };

在这里插入图片描述
当然,使用了虚继承需要付出代价,比如:虚继承的类对象会变大,虚基类的成员访问速度也会变慢,同时虚基类的初始化会比较复杂,它必须由最底层的派生类负责初始化,规则如下:

扫描二维码关注公众号,回复: 5023516 查看本文章

1、位于继承链最末端的子类的构造函数负责构造虚基类子对象。

2、虚基类的所有子类(无论直接的还是间接的)都必须在其构造函数中显式指明该虚基类子对象的构造方式,否则编译器将选择以缺省方式构造该子对象。

3、虚基类的所有子类(无论直接的还是间接的)都必须在其拷贝构造函数中显式指明以拷贝方式构造该虚基类子对象,否则编译器将选择以缺省方式构造该子对象。

ps建议平时使用的时候能不用虚继承就不要用,如果必须用的话尽量不要在虚基类中存放数据。

猜你喜欢

转载自blog.csdn.net/aiwangtingyun/article/details/86567256