linux C++ day10

目录:
智能手机
钻石继承
虚继承对钻石继承的改善
阻断继承类的静态实例化
电子文档阅读器
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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值