C++ 继承与接口 知识点 小结(二)

【摘要】【正文】检测并修改不适合的继承

理解a part of、派生、a kind of、和has some kind of

例:如果鸟是可以飞的,那么鸵鸟是鸟吗?鸵鸟如何继承鸟类?

分析:要鸵鸟来继承鸟类,采用组合的方法,把鸟类中的可以被鸵鸟继承的函数挑选出来,这样鸵鸟就不是 a kind of鸟了,而是has some kind of鸟的属性。

#include <iostream>#include <string>using namespace std;class bird{public:void eat();void sleep();void fly();};class ostrich{public:bird eat(){cout << "ostrich eat";};bird sleep(){cout << "ostrich sleep";};};int main(){ostrich xiaoq;xiaoq.eat();xiaoq.sleep();return 0;}

例:若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,而不是派生而成。程序如下:

class Eye{public:void Look(void);};class Nose{public:void Smell(void);};class Mouth{public:void Eat(void);};class Ear{public:void Listen(void);};class Head{public:void Look(void) { m_eye.Look(); }void Smell(void) { m_nose.Smell(); }void Eat(void) { m_mouth.Eat(); }void Listen(void) { m_ear.Listen(); }private:Eye m_eye;Nose m_nose;Mouth m_mouth;Ear m_ear;};Head由Eye、Nose、Mouth、Ear组合而成。如果允许Head从Eye、Nose、Mouth、Ear派生而成,那么Head将自动具有Look、Smell、Eat、Listen这些功能。程序十分简短并且运行正确,但是下面这种设计方法却是不对的。class Head : public Eye, public Nose, public Mouth, public Ear{};

具体设计模式的基础设计方式

详见:设计模式 创建型模式 知识点 小结

详址:

类继承中私有继承是无法继承并使用父类函数中的公有变量的

题:找出下面程序的错误,并解释它为什么是错的。

#include <iostream> using namespace std; class Base {public:int val;Base() { val=1;}; }; class Derive: Base {public:int val;Derive(int i) { val=Base::val+i; }; }; int main(int, char**, char**) {Derive d(10);cout<<d.Base::val<<endl<<d.val<<endl;return 0; }

答案:把class Derive: Base改成class Derive:public Base。

解析:这是个类继承问题。如果不指定public,C++默认的是私有继承。私有继承是无法继承并使用父类函数中的公有变量的。

子类中设定初始化成员变量

题:(找出下面程序的错误,并解释它为什么是错的。)

class base{ private: int i; public: base(int x){i=x;}};class derived: public base{ private: int i; public: derived(int x, int y) {i=x;}void printTotal() {int total = i+base::i;}};解析:要在子类中设定初始成员变量,把derived(int x, int y)改成derived(int x, int y) : base(x)。

答案如下:

class base{protected: //这里的访问属性需要改变int i;public: base(int x){i=x;}};class derived: public base{ private:int i; public: derived(int x, int y) : base(x) //以前没有初始化基类的成员变量 {i=y;} void printTotal() {int total = i+base::i; }};

纯虚函数

虚函数与纯虚函数的比较

详见:C++ 虚函数与纯虚函数 浅析

详址:

什么是虚指针?

虚指针是一个虚函数的实现细节,带有虚函数的类中的每一个对象都带有一个虚指针指向该类的虚函数表。

声明一个类Vehicle,使其称为抽象数据类型,写出类Car和Bus的声明,其中每个类都从类Vehicle里派生。使Vehicle成为一个带有两个纯虚函数的ADT,使Car和Bus不是ADT(抽象数据类型)

class Vehicle{public:virtual void Move()=0;virtual void Haul()=0;};class Car:public Vehicle{public:virtual void Move();virtual void Hual();};class Bus:public Vehicle{pulic:virtual void Move();virtual void Hual();};虚函数入口地址和普通函数有什么不同?

每个虚函数都在vtable中占了一个表项,,保存着一条跳转到它的入口地址的指令,当一个包含虚函数的对象被创建的时候,它的头部附加一个指针,指向vtable中相应的位置,调用虚函数的时候,不管你是用什么指针调用的,它先根据vtable找到入口地址再执行,从而实现了动态联编。

但是普通函数简单跳转到一个固定的地址。

补充:C++只有涉及到多态和虚拟函数就必须要使用动态联编,其他全是静态联编。

C++如何阻止一个类被实例化?

使用抽象类,或者构造函数被声明为private。

一般在什么时候构造函数被声明成 private 呢?

比如要阻止编译器生成默认的copy constructor的时候。

什么时候编译器会生成默认的生成copy constructor?

只要自己没写,而程序中需要,都会生成。

如果你写了一个构造函数,编译器还会生成copy constructor吗?

会生成。

运算符重载和RTTI运行时类型识别

C++引入的额外开销主要体现

1)编译时候的开销;

2)运行时的开销

①虚基类

②虚函数

③RTTI

④异常

⑤对象的构造和析构

运行类型识别RTTI使用需要注意的问题

在分布式系统中,不适用RTTI的一个合理的解释是RTTI行为不可预期及缺乏扩展性。

1)用typeid()返回一个typeinfo对象,也可以用于内部类型,当用用于非多态类型时没有虚函数,用typeid返回的将是基类地址;

2)不能对void指针进行映射;

3)如果p是指针,typeid(*p)返回p所指向的派生类类型,typeid(p)返回基类类型;如果r是引用,typeid(r)返回派生类类型,typeid(&r)返回基类类型;

4)C++里面的typeid运算符返回值是type_info常量对象的引用。

详见:C++ 宏、范型和RTTI 浅析

详址:

运算符重载

认识运算符重载

所谓重载,就是重新赋予新的含义,函数重载就是对一个已有的函数赋予新的含义,使之实现新功能

运算符的重载主要存在两种形式,一种是作为类的成员函数进行使用,另一种则是作为类的友元函数进行使用。

运算符的重载的形式:

返回类型 operator 运算符符号(参数说明){//函数体的内部实现}

例如,能否用“+”号进行两个复数的相加,在C++中不能在程序中直接用运算符“+”对复数进行相加运算,用户必须自己设法实现复数相加。

运算符重载运算符的运算规则

1)运算符重载函数也是函数,重载的运算符不会改变运算符的优先级、结合型和参数的个数。

2)重载运算符不能违反语言的语法规则。

3)赋值运算符除外,重载运算符可由派生类继承下去。

4)重载运算符不能使用默认参数。

5)运算符=、()、[]和->等操作符必须定义为类的成员函数,将这些操作符定义为友元函数将在编译时标记为错误。

6)C++中不能重载的运算符只有5个:

. (成员访问运算符)

.* (成员指针访问运算符)

∷ (域运算符)

sizeof(长度运算符)

?: (条件运算符)

因为前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof运算符的运算对象是类型而不是变量或一般表达式,不具重载的特征

以一种进取的和明智的方式同它们奋斗。

C++ 继承与接口 知识点 小结(二)

相关文章:

你感兴趣的文章:

标签云: