effective c++条款32~40“继承与面向对象设计”整理

条款32:确定你的public继承塑模出is-a关系

以C++进行面向对象编程,最重要的一个规则是:public inheritance(公有继承)意味is-a(是一种)的关系。

在C++领域中,任何函数如果期望获得一个类型为基类的实参(而不管是传指针或是引用),都也愿意接受一个派生类对象(而不管是传指针或是引用)。(只对public继承才成立。)好的接口可以防止无效的代码通过编译,因此你应该宁可采取“在编译期拒绝”的设计,而不是“运行期才侦测”的设计。is a并不是唯一存在classes之间的关系。另两个常见的关系是has-a(有一个)和is-implemented-in-term-of(根据某物实现出)。

请记住:

“public继承”意味is-a。适用于base class身上的每一件事情一定也适用于derived class身上,因为每一个derived class对象也都是一个base class对象。

条款33:避免遮掩继承而来的名称

在继承体系下,派生类的作用域包含在基类作用域下,因而在派生类中的同名变量会遮掩基类的变量。

class Base{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};

class Derived : public Base{
public:
virtual void mf1();
void mf3();
void mf4();
...
};

Derived d;
intx;
...
d.mf1(); //ok,调用Derived::mf1()
d.mf1(x); //error
d.mf2(); //ok,调用Base::mf2
d.mf3(); //ok,调用Derived::mf3
d.mf3(x); //error

在基类中所有名为mf1和mf3的函数都被派生类中的同名函数遮掩掉了,即使它们有不同的参数类型,即使它们是虚函数或非虚函数。

由于上面是public继承,为了能够使用基类中的同名函数(也就是保持is-a关系),可以采用两种办法:使用using声明式或转交函数。

1.在上面的例子中,派生类可做以下修改:
class Derived : public Base{
public:
using Base::mf1; //让Base class内名为mf1和mf3的所有东西中
using Base::mf3; //在Derived作用域中可见
virtual void mf1();
void mf3();
void mf4();
...
};
Derived d;
intx;
...
d.mf1(); //ok,调用Derived::mf1()
d.mf1(x); //OK,调用Base::mf1(int)
d.mf2(); //ok,调用Base::mf2()
d.mf3(); //ok,调用Derived::mf3()
d.mf3(x); //ok,调用Base::mf3(int)
这就意味着如果继承基类并想重载基类函数,而你又希望重新定义或覆写其中一部分,那么为那些原本会被遮掩的每个名称引入以一个using声明式。

2.如果试图选择性地只继承部分重载函数,这在public继承下不可能发生,因为它违反了public继承所暗示的is-a关系,然而在private继承下可能有意义。例如上式Derived以private继承base,而Derived唯一想继承的mf1是那个无参数版本。using声明式这里没用,因为他声明的某给定名称的所有同名函数都

在Derived中可见,可以使用转交函数(forwarding function):
class Derived : private Base{ //私有继承,is-implemented-in-term-of关系
public:
virtual void mf1(){ //转交函数
Base::mf1();
}
...
};

Derived d;
intx;
...
d.mf1(); //ok,调用Derived::mf1()
d.mf1(x); //error,Base::mf1被屏蔽

请记住:

derived calsses内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。

为了让被遮掩的名称再见天日,可使用using声明式或转交函数(forwarding function)。

条款34:区分接口继承和实现继承

表面上直截了当的public继承概念,经过更严密的检查之后,发现它由两部分组成:函数接口继承和函数实现继承。成员函数的接口总是会被继承。

pure virtual函数有两个最突出的特性:它们必须被任何“继承了它们”的具象class重新声明,而且它们在抽象class中通常没有定义。所以:声明一个pure virtual函数的目的是为了让derived class只继承函数接口。

令人意外的是,我们竟然可以为pure virtual函数提供定义。但调用它的唯一途径是“调用时明确指出其class名称”:

声明简朴的(非纯)impure virtual函数的目的,是让derived class继承该函数的接口和缺省实现。

声明non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现。

如果成员函数是个non-virtual函数,意味着它并不打算在derived classes中有不同的行为。

请记住:

接口继承和实现继承不同。在public继承之下,derived classes总是继承base class的接口。

pure virtual函数只具体制定接口继承。

简朴的(非纯)impure virtual函数具体制定接口继承及缺省实现继承。

non-virtual函数具体制定接口继承以及强制性实现继承。

条款35:考虑virtual函数以外的其它选择

条款36:绝不重新定义继承而来的non-virtual函数

请记住:

绝对不要重新定义继承而来的non-virtual函数。

条款37:绝不重新定义继承而来的缺省参数值

请记住:

绝对不要重新定义继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数——你唯一应该覆写的东西——却是动态绑定。

条款39:明智而审慎地使用private继承

请记住:

1.private继承意味is-implementation-in-terms of(根据某物实现出)。她通常比复合级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。

2.和复合不同,private继承可以造成empty base最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要。

条款40:明智而审慎地使用多重继承

请记住:

1.多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需求。

2.virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base class不带任何数据,将是最具实用价值的情况。

3.多重继承的确有正当用途。其中一个情节涉及“public继承某个Interface class”和“private继承某个协助实现的class”的两相组合。

时间: 2024-11-08 16:23:31

effective c++条款32~40“继承与面向对象设计”整理的相关文章

Effective C++笔记06:继承与面向对象设计

关于OOP 博客地址:http://blog.csdn.net/cv_ronny 转载请注明出处! 1,继承可以是单一继承或多重继承,每一个继承连接可以是public.protected或private,也可以是virtual或non-virtual. 2,成员函数的各个选项:virtual或non-virtual或pure-virtual. 3,成员函数和其他语言特性的交互影响:缺省参数值与virtual函数有什么交互影响?继承如何影响C++的名称查找规则?设计选项有如些?如果class的行为

【C++常识】effective C++ 使用条款——内存管理/继承和面向对象设计/杂项

第六章 继承和面向对象设计 条款35: 使公有继承体现 "是一个" 的含义 1.子类对象一定"是一个"基类对象,基类对象不一定是子类对象 条款36: 区分接口继承和实现继承 1.希望派生类只继承成员函数的接口--将函数声明为纯虚函数 2.希望派生类同时继承函数的接口和实现,但允许派生类改写实现--将函数声明为虚函数 3.希望同时继承接口和实现,并且不允许派生类改写任何东西--声明为非虚函数 条款37: 决不要重新定义继承而来的非虚函数 1.基类声明为非虚的函数表示这

继承和面向对象设计

继承与面向对象设计 32. 确定你的public继承塑模出is-a关系 "public继承"意味is-a.适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象 33. 避免遮掩继承而来的名称 derived classes 内的名称会遮掩base classes 内的名称.在public继承下从来没有人希望如此. 为了让被遮掩的名称再见天日,可使用using 声明式或转交

【Effective C++】继承与面向对象设计

关于OOP 1,继承可以是单一继承或多重继承,每一个继承连接可以是public.protected或private,也可以是virtual或non-virtual. 2,成员函数的各个选项:virtual或non-virtual或pure-virtual. 3,成员函数和其他语言特性的交互影响:缺省参数值与virtual函数有什么交互影响?继承如何影响C++的名称查找规则?设计选项有如些?如果class的行为需要修改,virtual函数是最佳选择吗? 4,public继承意味着“is-a”. 5

Effective C++ 6.继承与面向对象设计

//条款32:确定你的public继承塑模出is-a关系 // 1.public继承意味着is-a的关系,适用在基类上的方法都能用于派生类上. //条款33:避免遮掩继承而来的名称 // 1.在public继承体系中,派生类和基类的关系是is-a的关系,所以派生类中不应该隐藏基类的非虚函数. // 2.为了在派生类中重载基类的非虚函数,可以使用using声明式,或者在派生类的函数中显示调用基类的非虚函数. //条款34:区分接口继承和实现继承 // 1.接口继承与实现继承不同.在public继承

Effective C++(20) 继承与面向对象设计

本文主要参考<Effective C++ 3rd>中的第六章部分章节的内容. 关注的问题集中在继承.派生.virtual函数等.如: virtual? non-virtual? pure virtual? 缺省参数值与virtual函数有什么交互影响? 继承如何影响C++的名称查找规则? 什么情况下有比virtual更好的选择? 这些都是我们将要从这一章里学到的内容. 1 确定你的public继承可以塑模出is-a关系 谨记public继承的含义: 如果class D以public形式继承cl

Effective C++笔记(六):继承与面向对象设计

参考:http://www.cnblogs.com/ronny/p/3756494.html 条款32:确定你的public继承塑模出is-a关系 “public继承”意味着is-a.适用于base classes身上的每一件事情一定也适合于derived class身上,因为每个derived classes对象也都是一个base classes对象. 条款33:避免遮掩继承而来的名称 derived classes内的名称会遮掩base classes内的名称. Derived d; int

Effective C++ 条款32 确定你的public继承塑模出is-a关系

1. public继承意味着"is-a"(是一个)关系,是接口的完全继承(不一定是接口实现的完全继承).例如B继承自A,如果采用public继承,那么意味着B是A的一种,因此A可以进行的操作B同样也可以进行(尽管实现可能不同). 2. public继承的is-a含义(基类可以进行的操作派生类同样可以进行)与现实生活中的is-a含义可能有些许不同.例如,在生活中,正方形无疑是矩形的一种,因此可以认为是is-a关系;然而如果用public继承的is-a含义来解释,正方形可能就不可以publ

Effective C++ -- 继承和面向对象设计

32.确保你的public继承了模is-a关系 public继承意味着is-a关系(里氏替换原则),一切适用于基类也适用于派生类. 矩形继承正方形问题: 可实施与矩形的操作无法实施与正方形 在编程领域.正方形是一种矩形是错误的 在现实领域,正方形是一种矩形是正确的 33.避免遮盖继承而来的名称 class Base { private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2()