目录:
智能手机
钻石继承
虚继承对钻石继承的改善
阻断继承类的静态实例化
电子文档阅读器
1 智能手机
1.1 问题
一个类可以同时从多个基类继承实现代码。如果在子类的多个基类中,存在同名的标识符,而且子类又没有隐藏该名字,那么任何试图在子类中,或通过子类对象访问该名字的操作,都将引发歧义,除非通过作用域限定操作符“::”显式指明所属基类。如果无法避免基类中的名字冲突,最简单的方法是在子类中隐藏这些标识符,或借助using声明令其在子类中重载。

1.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:智能手机

代码如下所示:

#include
class Computer
{
public:
void show (Computer* c = NULL) const
{
std::cout << “Computer” << std::endl;
}
};
class Telephone
{
public:
void show (Telephone* t = NULL) const
{
std::cout << “Telephone” << std::endl;
}
};
class MobilePhone : public Computer, public Telephone
{

};
int main(int argc, const char * argv[])
{

MobilePhone mp;
mp.show();return 0;

}
上述代码中,以下代码:

class Computer
{
public:
void show (Computer* c = NULL) const
{
std::cout << “Computer” << std::endl;
}
};
class Telephone
{
public:
void show (Telephone* t = NULL) const
{
std::cout << “Telephone” << std::endl;
}
};
定义了两个类,类Computer和类Telephone,其中各自定义了一个成员函数show。这是正确的。因为类的封装性,两个函数show分别在两个不同的作用域中。

上述代码中,以下代码:

class MobilePhone : public Computer, public Telephone
{

};
定义了一个子类Mobilephone,多重继承了类Computer和类Telephone。这样就带来了一个问题,就是子类Mobilephone中有两个成员函数show,一个继承自类Computer,另一个继承自类Telephone。当在主程序中,出现如下语句时:

MobilePhone mp;
mp.show();

就会出现编译错误,因为编译器不知道mp.show()调用哪一个show。只能通过作用域限定操作符“::”显式指明所属基类,如以下语句所示:

MobilePhone mp;
mp.Computer::show();

或者借助using声明令其在子类中重载。如将子类Mobilephone定义成如下形式:

class MobilePhone : public Computer, public Telephone
{
using Computer::show;
using Telephone::show;
};
此时,两个成员函数将变成重载的关系。

1.3 完整代码
本案例的完整代码如下所示:

#include
class Computer
{
public:
void show (Computer* c = NULL) const
{
std::cout << “Computer” << std::endl;
}
};
class Telephone
{
public:
void show (Telephone* t = NULL) const
{
std::cout << “Telephone” << std::endl;
}
};
class MobilePhone : public Computer, public Telephone
{
};
int main(int argc, const char * argv[])
{

MobilePhone mp;
mp.Computer::show();return 0;

}
2 钻石继承
2.1 问题
一个子类继承自多个基类,而这些基类又源自共同的祖先(公共基类),这样的继承结构称为钻石继承。派生多个中间子类的公共基类子对象,在继承自多个中间子类的汇聚子类对象中,存在多个实例,在汇聚子类中,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致不一致。

2.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:钻石继承

代码如下所示:

#include
class Base
{
public:
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : public Base
{

};
class MidDerive2 : public Base
{

};
class Derive : public MidDerive1, public MidDerive2
{

};
int main(int argc, const char * argv[])
{

Derive d;
//d.show();return 0;

}
上述代码中,以下代码:

class Base
{
public:
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : public Base
{

};
class MidDerive2 : public Base
{

};
定义了一个类Base,由它派生出两个子类,类MidDerive1和类MidDerive2,其中各自继承了基类Base中的成员函数show。这是正确的。因为类的封装性,三个函数show分别在三个不同的类作用域中。

上述代码中,以下代码:

class Derive : public MidDerive1, public MidDerive2
{

};
定义了一个子类Derive,多重继承了类MidDerive1和类MidDerive2。这样就带来了一个问题,就是子类Derive中有两个成员函数show,一个继承自类MidDerive1,另一个继承自类MidDerive2。当在主程序中,出现如下语句时:

Derive d;
//d.show();

就会出现编译错误,因为编译器不知道d.show()调用哪一个show。只能通过作用域限定操作符“::”显式指明所属基类,如以下语句所示:

MobilePhone mp;
d.MidDerive1::show();

2.3 完整代码
本案例的完整代码如下所示:

#include
class Base
{
public:
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : public Base
{

};
class MidDerive2 : public Base
{

};
class Derive : public MidDerive1, public MidDerive2
{

};
int main(int argc, const char * argv[])
{

Derive d;
d.MidDerive1::show();return 0;

}
3 虚继承对钻石继承的改善
3.1 问题
通过虚继承,可以保证公共基类子对象在汇聚子类对象中,仅存一份实例,且为多个中间子类子对象所共享。为了表示虚继承,需要在继承表中使用virtual关键字。

一般而言,子类的构造函数不能调用其间接基类的构造函数。但是,一旦这个间接基类被声明为虚基类,它的所有子类(无论直接的还是间接的)都必须显式地调用该间接基类的构造函数。否则,系统将试图为它的每个子类对象调用该间接基类的无参构造函数。

3.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:虚继承对钻石继承的改善

代码如下所示:

#include
class Base
{
private:
int m_a;
public:
Base (const int& a) : m_a(a)
{
}
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : virtual public Base
{
public:
MidDerive1 (const int& a) : Base(a)
{
}
};
class MidDerive2 : virtual public Base
{
public:
MidDerive2 (const int& a) : Base(a)
{
}
};
class Derive : public MidDerive1, public MidDerive2
{
public:
Derive (const int& a = 0) : MidDerive1(a), MidDerive2(a), Base(a)
{
}
};
int main(int argc, const char * argv[])
{

Derive d;
d.show();return 0;

}
上述代码中,以下代码:

class Base
{
private:
int m_a;
public:
Base (const int& a) : m_a(a)
{
}
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : virtual public Base
{
public:
MidDerive1 (const int& a) : Base(a)
{
}
};
class MidDerive2 : virtual public Base
{
public:
MidDerive2 (const int& a) : Base(a)
{
}
};
定义了一个类Base,由它派生出两个子类,类MidDerive1和类MidDerive2,其中各自继承了基类Base中的成员函数show。这是正确的。因为类的封装性,三个函数show分别在三个不同的类作用域中。值得注意的是,类MidDerive1和类MidDerive2继承基类Base,在继承表中多了一个关键字virtual,这样就构成了虚继承的概念。

上述代码中,以下代码:

class Derive : public MidDerive1, public MidDerive2
{
public:
Derive (const int& a = 0) : MidDerive1(a), MidDerive2(a), Base(a)
{
}
};
定义了一个子类Derive,多重继承了类MidDerive1和类MidDerive2。由于通过虚继承,可以保证公共基类Base在汇聚子类Derive中,仅存一份成员函数show,且为多个中间子类MidDerive1和MidDerive2所共享。所以,在如下主函数程序中:

Derive d;
d.show();

不会出现编译错误。

一般而言,子类Derive的构造函数不能调用其间接基类Base的构造函数。但是,一旦这个间接基类被声明为虚基类,它的所有子类(无论直接的还是间接的)都必须显式地调用该间接基类的构造函数。否则,系统将试图为它的每个子类对象调用该间接基类的无参构造函数。如以下代码所示:

Derive (const int& a = 0) : MidDerive1(a), MidDerive2(a), Base(a)
{
}

3.3 完整代码
本案例的完整代码如下所示:

#include
class Base
{
private:
int m_a;
public:
Base (const int& a) : m_a(a)
{
}
void show (void) const
{
std::cout << “Base” << std::endl;
}
};
class MidDerive1 : virtual public Base
{
public:
MidDerive1 (const int& a) : Base(a)
{
}
};
class MidDerive2 : virtual public Base
{
public:
MidDerive2 (const int& a) : Base(a)
{
}
};
class Derive : public MidDerive1, public MidDerive2
{
public:
Derive (const int& a = 0) : MidDerive1(a), MidDerive2(a), Base(a)
{
}
};
int main(int argc, const char * argv[])
{

Derive d;
d.show();return 0;

}
4 阻断继承类的静态实例化
4.1 问题
通过将类的构造函数私有化,可以禁止该类被继承,因为子类必须能够调用基类的构造函数。但该类本身也因此无法被静态实例化,只能通过静态接口动态地创建,而这样极可能因为忘记delete而导致内存泄漏。

为了解决这个矛盾,可从该类公有派生一个友元子类,子类是基类的友元,可访问其私有部分,且可被静态实例化。但该友元子类亦可被继承,虽然禁止用户定义阻断继承类的直接子类,但仍无法禁止用户定义其间接子类。为此,可将友元子类从阻断继承类的继承改为虚继承,即令阻断继承类成为其友元子类的虚基类。

4.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:阻断继承类的静态实例化

代码如下所示:

#include
class User
{
private:
std::string m_name;
User (std::string const& name) : m_name(name)
{
}
public:
static User* createInstance (std::string const& name)
{
return new User(name);
}
friend class UserWrapper;
};
class UserWrapper : public User
{
public:
UserWrapper (std::string const& name) : User (name)
{
}
};
int main(int argc, const char * argv[])
{

//User user("张飞");
User *pUser = User::createInstance("张飞");
delete pUser;UserWrapper user("张飞");return 0;

}
上述代码中,以下代码:

class User
{
private:
std::string m_name;
User (std::string const& name) : m_name(name)
{
}
通过将类User的构造函数私有化,可以禁止该类被继承,因为子类必须能够调用基类的构造函数。但类User本身也因此无法被静态实例化,如以下主程序中的语句:

//User user("张飞");

编译错误。为了使用该类的对象,只能通过如下语句:

static User* createInstance (std::string const& name)
{return new User(name);
}

定义类User的静态接口动态地创建,如以下主程序中的语句:

User *pUser = User::createInstance("张飞");

但这样做极可能因为忘记通过delete释放pUser指向的空间而导致内存泄漏。为了解决这个问题,可从类User公有派生一个子类,如下代码所示:

class UserWrapper : public User
{
public:
UserWrapper (std::string const& name) : User (name)
{
}
};
但因为类User的构造函数是私有的,所以类User被禁止继承,但可以将该子类UserWrapper声明为子类是基类User的友元,如以下在类User中的代码:

friend class UserWrapper;

这样,子类UserWrapper就可访问类User的私有部分,且可被静态实例化了,如以下主程序中的语句:

UserWrapper user("张飞");

是正确的。但该友元子类UserWrapper却可以被继承,这样虽然禁止用户定义阻断继承类User的派生子类,但仍无法禁止用户定义其间接子类,即友元子类UserWrapper派生子类。为此,可将友元子类UserWrapper从阻断继承类User的继承改为虚继承,即令阻断继承类成为其友元子类的虚基类,如以下友元子类UserWrapper的定义:

class UserWrapper : virtual public User
{
public:
UserWrapper (std::string const& name) : User (name)
{
}
};
这样友元子类UserWrapper的任何派生类中的构造函数中都必须 (显式或隐式)调用类User的构造函数,致使编译器报错,而阻断了友元子类UserWrapper派生子类。

4.3 完整代码
本案例的完整代码如下所示:

#include
class User
{
private:
std::string m_name;
User (std::string const& name) : m_name(name)
{
}
public:
static User* createInstance (std::string const& name)
{
return new User(name);
}
friend class UserWrapper;
};
class UserWrapper : virtual public User
{
public:
UserWrapper (std::string const& name) : User (name)
{
}
};
int main(int argc, const char * argv[])
{

//User user("张飞");
User *pUser = User::createInstance("张飞");
delete pUser;UserWrapper user("张飞");return 0;

}
5 电子文档阅读器
5.1 问题
虚函数是指在类的成员函数声明之前添加了关键字virtual的成员函数。

如果子类提供了对基类虚函数的有效覆盖,那么通过一个指向子类对象的基类指针,或者引用子类对象的基类引用,调用该虚函数,实际被调用的将是子类中的覆盖版本,而非基类中的原始版本,这种语法现象称为多态。

多态的重要意义在于,一般情况下,调用哪个类的成员函数是由调用者指针或引用本身的类型决定的,而当多态发生时,调用哪个类的成员函数则完全由调用者指针或引用的实际目标对象的类型决定。

5.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:电子文档阅读器

代码如下:

#include
class Reader
{
public:
virtual void display (void) const
{
std::cout << “请选择一本书” << std::endl;
}
};
class HongLouMeng : public Reader
{
public:
void display (void) const
{
std::cout << “世事洞明皆学问,人情练达即文章。” << std::endl;
std::cout << “假作真时真亦假,无为有处有还无。” << std::endl;
std::cout << “春恨秋悲皆自惹,花容月貌为谁妍。” << std::endl;
}
};
class SanGuoYanYi : public Reader
{
public:
void display (void) const
{
std::cout << “滚滚长江东逝水,浪花淘尽英雄。” << std::endl;
std::cout << “是非成败转头空。” << std::endl;
std::cout << “青山依旧在,几度夕阳红。” << std::endl;
std::cout << “白发渔樵江渚上,惯看秋月春风。” << std::endl;
std::cout << “一壶浊酒喜相逢。” << std::endl;
std::cout << “古今多少事,都付笑谈中。” << std::endl;
}
};
void read (Reader const* reader)
{
reader->display();
}
int main(int argc, const char * argv[])
{

HongLouMeng hlm;
SanGuoYanYi sgyy;read(&hlm);
read(&sgyy);return 0;

}
上述代码中,以下代码:

class Reader
{
public:
virtual void display (void) const
{
std::cout << “请选择一本书” << std::endl;
}
};
定义了电子文档阅读器类Reader,在该类中定义了一个成员函数display,在该函数定义的前面添加了关键字virtual,说明该函数是一个虚函数。

上述代码中,以下代码:

class HongLouMeng : public Reader
{
public:
void display (void) const
{
std::cout << “世事洞明皆学问,人情练达即文章。” << std::endl;
std::cout << “假作真时真亦假,无为有处有还无。” << std::endl;
std::cout << “春恨秋悲皆自惹,花容月貌为谁妍。” << std::endl;
}
};
定义了电子文档阅读器类Reader的子类HonglouMeng。由于子类HonglouMeng的成员函数display和基类Reader的虚函数display具有相同的函数原型,所以子类HonglouMeng的成员函数display也是虚函数,无论其是否带有virtual关键字,且对基类的虚函数构成覆盖。

上述代码中,以下代码:

class SanGuoYanYi : public Reader
{
public:
void display (void) const
{
std::cout << “滚滚长江东逝水,浪花淘尽英雄。” << std::endl;
std::cout << “是非成败转头空。” << std::endl;
std::cout << “青山依旧在,几度夕阳红。” << std::endl;
std::cout << “白发渔樵江渚上,惯看秋月春风。” << std::endl;
std::cout << “一壶浊酒喜相逢。” << std::endl;
std::cout << “古今多少事,都付笑谈中。” << std::endl;
}
};
定义了电子文档阅读器类Reader的子类SanGuoYanYi。由于子类SanGuoYanYi的成员函数display和基类Reader的虚函数display具有相同的函数原型,所以子类SanGuoYanYi的成员函数display也是虚函数,也对基类的虚函数构成覆盖。

上述代码中,以下代码:

read(&hlm);

是调用read函数,该函数的定义如下:

void read (Reader const* reader)
{
reader->display();
}
这样就将使得函数read的形参(基类Reader的指针reader)指向了实参(子类HonglouMeng的对象hlm)的地址。那么通过一个指向子类对象hlm的基类指针read,调用虚函数display,实际被调用的将是子类HonglouMeng中的覆盖版本,而非基类Reader中的原始版本。以下代码同理:

read(&sgyy);

从而实现了多态。

5.3 完整代码
本案例的完整代码如下所示:

#include
class Reader
{
public:
virtual void display (void) const
{
std::cout << “请选择一本书” << std::endl;
}
};
class HongLouMeng : public Reader
{
public:
void display (void) const
{
std::cout << “世事洞明皆学问,人情练达即文章。” << std::endl;
std::cout << “假作真时真亦假,无为有处有还无。” << std::endl;
std::cout << “春恨秋悲皆自惹,花容月貌为谁妍。” << std::endl;
}
};
class SanGuoYanYi : public Reader
{
public:
void display (void) const
{
std::cout << “滚滚长江东逝水,浪花淘尽英雄。” << std::endl;
std::cout << “是非成败转头空。” << std::endl;
std::cout << “青山依旧在,几度夕阳红。” << std::endl;
std::cout << “白发渔樵江渚上,惯看秋月春风。” << std::endl;
std::cout << “一壶浊酒喜相逢。” << std::endl;
std::cout << “古今多少事,都付笑谈中。” << std::endl;
}
};
void read (Reader const* reader)
{
reader->display();
}
int main(int argc, const char * argv[])
{

HongLouMeng hlm;
SanGuoYanYi sgyy;read(&hlm);
read(&sgyy);return 0;

}

Linux c++ day09相关推荐

  1. Linux课程笔记 Day09 课上内容总结 MySql,Php的安装及Apache,Nginx,Php的优化

    一 MySql 1.1    如何选择MySql的版本 1.2   MySql单实例安装 (1)       建立mysql用户 首先以root身份登陆到linux系统,然后执行如下命令创建mysql ...

  2. Day09 红帽Linux — 2控制对文件的访问

    红帽Linux - 2控制对文件的访问 文章目录 红帽Linux - 2控制对文件的访问 一.Linux文件系统权限 权限类型 权限的表示 二.管理文件系统权限 修改文件权限 chmod指令结构 赋予 ...

  3. Day09 红帽Linux — 1管理本地用户和组

    红帽Linux - 1管理本地用户和组 文章目录 红帽Linux - 1管理本地用户和组 一.组的概念 组类型 使用 id 命令可以查看用户的主要组和补充组 组信息 /etc/group /etc/g ...

  4. linux第一阶段学习笔记基础

    linux学习 day01 1.计算机基础 运维人员的职责: 7*24是服务器稳定运行 数据不能丢失损坏 提升用户体验 常见的服务器种类 DELL DELL 1U 2U 2010 1850/1950 ...

  5. Python学习笔记--day09 函数 模块 (文件、路径操作)

    第二模块 函数&模块 第一模块主要是学习python基础知识,从第二模块开始就可以通过程序去解决工作中实际的问题. 函数,一个用于专门实现某个功能的代码块(可重用). 内置函数 len.bin ...

  6. Linux黑客基础02篇(部署镜像及有关命令的操作)

    Kali Linux部署镜像及命令操作 使用虚拟机文件部署kali Linux default credentials "kali/kali" *.vmx 虚拟机配置文件 *.vm ...

  7. linux第二阶段架构

    综合架构 day01 综合架构原理 1.企业上网原理 路由器配置:第一步:获取设备管理IP地址,修改笔记本网卡地址第二步:外网线路配置(运营商),完成路由或拨号配置(用户名 密码-- 获取外网IP)第 ...

  8. 过滤Linux下不同大小的文件,linux查找当前目录下 M/G 大小的文件,删除Linux下指定大小的文件

    过滤Linux下不同大小的文件,linux查找当前目录下 M/G 大小的文件,删除Linux下指定大小的文件 find ./ -type f -size +1G| xargs rm 在清理系统日志文件 ...

  9. linux环境下nacos的安装+启动,阿里云服务器安装nacos

    nacos安装+启动(linux环境): 基础:安装java环境 官网下载压缩包:如 nacos-server-1.2.1.tar.gz 放在自定义目录下 # 解压 tar -xvf nacos-se ...

最新文章

  1. Cisco ××× 完全配置指南-连载-IPSec
  2. iOS 自动构建套件 - flow.ci + fir.im + Coding
  3. Java 第一个Java程序
  4. 图解动软代码生成器使用
  5. 实录 | 平安人寿资深算法工程师姚晓远:对话生成模型的探析与创新
  6. PHP计算表达式-栈
  7. 蓝桥杯基础模块3_1:数码管静态显示
  8. A4Desk 网站破解
  9. ES 必备插件的安装
  10. html 自定义文件选择,html5 自定义文件上传
  11. 桌面计算机打不开win8.1,Win8.1怎么进桌面 2种Win8.1开机直接进桌面方法
  12. 搜索引擎与信息处理 复习笔记
  13. foobar2000 for mac(经典音乐播放器)
  14. 是谁在觊觎娱乐圈站点?揭秘神秘黑客组织-黑界
  15. java登陆界面背景_Java登录界面的实现(注册、登录、背景图片)
  16. css+div布局实现简历界面
  17. phpMyAdmin无法缓存模板文件,所以会运行缓慢。
  18. 运维笔记之调用163邮箱发送邮件
  19. ffmpeg开发之旅(4):MP3编码格式分析与lame库编译封装
  20. Chocolatey--windows包管理器的下载和使用(超详细)

热门文章

  1. 基于物联网的智能厨房安全监测系统-总论
  2. qt客户端显示服务器发送的图片,c++ - Qt客户端服务器应用程序“发送图像”出现问题 - 堆栈内存溢出...
  3. Eclipse启动和手动启动tomcat访问localhost:8080显示404问题总结
  4. 扩展卡尔曼滤波建模及应用
  5. AMap行政区查询服务
  6. 2022年信息系统运行管理员考试大纲
  7. 佐糖推出图片变清晰API,图像清晰度增强接口API
  8. 投诉申告:招商银行信用卡岂能如此无信用?!
  9. 《破坏之王—DDoS攻击与防范深度剖析》
  10. 树莓派基础实验6:轻触开关按键实验