C++学习:第三章C++语言基础 - (十)多重继承、虚继承、多态、typeinfo

1.  继承

一个类的构造函数总是会先调用他父类的构造函数再执行它自身的代码。

多个父类按照继承顺序执行构造函数。

数据二义性问题:如果父类中有重复的变量或者方法函数,且在子类中没有。则在运行调用时会出错,编译器不知该调用哪个。解决方法:

  1. 对象.父类名::同名方法/变量 这样调用 mp.MP3::getPrice();
  2. 将公共部分提出来作为一个单独的类,并虚继承。虚继承保证被继承的对象只被实现一次。虚基类的构造函数由底层子类直接传递
  3. 在构造函数和析构函数中没有多态:在构造函数中,父类还没有创建,因此无法调用虚函数。在析构函数执行时,子类已经析构完成,因此子类对象已经释放,所以父类执行析构时就无法执行析构函数了。
#include <iostream>

using namespace std;


class Camera{
	double price;

public:
	void take(const char* obj){
		cout << "给" << obj << "照相" << endl;
	}
	Camera(double p):price(p){cout<<"Camera()"<<endl;}
	~Camera(){cout<<"~Camera()"<<endl;}
	double getPrice(){return price;} 
};


class MP3{
	double price;
public:
	void play(const char* song){
		cout << "播放歌曲《" << song << "》" << endl;
	}
	MP3(double p):price(p){
        cout<<"MP3()"<<endl;
    }
	~MP3(){
        cout<<"~MP3()"<<endl;
    }
	double getPrice(){return price;} 
};


class Phone{
	double price;

public:
	void dial(const char* no){
		cout << "给" << no << "拨打电话" << endl;
	}
	Phone(double p):price(p){
        cout<<"Phone()"<<endl;
    }
	~Phone(){cout<<"~Phone()"<<endl;}
	double getPrice(){return price;} 
};


class ModernPhone : public MP3,public Phone,public Camera
{
	string factory;
public:

	void visitnet(const char* url){
		cout << "访问网址" << url << endl;
	}

	ModernPhone(const char* factory,double price)
	    :MP3(price),Phone(price),Camera(price),factory(factory){
		cout << "ModernPhone()" << endl;
	}
	~ModernPhone(){cout<<"~ModernPhone()"<<endl;}
};


int main()
{
	ModernPhone mp("nokia",500);
	mp.dial("110");
	mp.play("北京欢迎你");
	mp.visitnet("bbs.tarena.com.cn");
	mp.take("康森林");
    //多个价格冲突的情况下如何访问的问题
	cout << mp.Phone::getPrice() << endl;
}

2. 虚继承

在类型继承中出现的数据二义性问题,使得数据访问时变得复杂,并且导致了数据冗存。虚继承则解决了从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题。

关键字:virtual

用法:将共同基类声明设置为虚基类,这时从不同路径继承过来的同名数据成员在内存中只有一份,同一个函数名也只有一个映射。

语法:

class 派生类: virtual 基类1,virtual 基类2,…,virtual 基类n
{
    …//派生类成员声明
};

构造函数与析构函数的执行次序:

先执行虚基类的构造函数,多个虚基类按照被继承的次序顺序构造。

执行基类的构造函数,如果有多个基类则按照被声明次序依次构造。

执行成员对象的构造函数,多个成员对象按照声明次序构造。

执行派生类自己的构造函数。

析构函数与构造次序相反。

注意:

只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。

在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

下面代码例子中Camera 、MP3、Phone、MordenPhone 都继承了共同的虚基类,因此在实例化的过程中都会初始化父类的Price变量,这时就会有冲突,调用getPrice时就会有问题。结果是显示默认值123.45,最后price只保留了一份。因此在虚继承中规定:虚基类中的构造函数的参数由底层子类直接传递。Camera 类中的

Camera(double price):GOODS(price){cout << "Camera()" << endl; };
#include <iostream>
#include <string>

using namespace std;


class GOODS{
    double price;
public:
    GOODS(double p = 123.456):price(p){}
    ~GOODS(){}
    void getPrice(){
        cout << "price : " << price << endl; 
    }
};



class Camera : virtual public GOODS{
    double price;
public:
    void takePhoto(const char* obj){
        cout << "给 " << obj << " 照相" << endl; 
    }

    Camera(double price):GOODS(price){
        cout << "Camera()" << endl;
    }

    ~Camera(){
        cout << "~Camera()" << endl;
    }
};



class MP3 : virtual public GOODS{
    double price;
public:
    void play(const char* song){
        cout << "播放歌曲《" << song << "》" << endl;
    }
    MP3(double price):GOODS(price){
        cout << "MP3()" << endl;
    }
    ~MP3(){
        cout << "~MP3()" << endl;
    }
};



class Phone : virtual public GOODS{
    double price;
public:
    void dial(const char* no){
        cout << "给 " << no << " 拨打电话" << endl;
    }
    Phone(double price):GOODS(price){
        cout << "Phone()" << endl;
    }
    ~Phone(){
        cout << "~Phone()" << endl; 
    }
};

class MordenPhone : public Phone, public Camera, public MP3{
    string factory;
public:
    void visitNet(const char* url){
    cout << "访问网址:" << url << endl;
    }

    MordenPhone(const char* factory, double price):
        factory(factory), MP3(price), Phone(price), Camera(price),GOODS(price){
        cout << "MordenPhone()" << endl; }
    ~MordenPhone(){
        cout << "~MordenPhone()" << endl; 
    }
};


int main(){

    MordenPhone mp("Nokia", 500);
    mp.dial("12345");
    mp.play("北京欢迎你");
    mp.visitNet("www.baidu.com");
    mp.takePhoto("帅哥");
    mp.getPrice();

    getchar();
    return 0;
}

虚函数

C++中,虚函数用来实现多态。

关键字:virtual

如果一个类包含了虚函数,那么在创建对象时会额外增加一张表,表中的每一项都是虚函数的入口地址。这张表就是虚函数表,也称为 vtable。 可以认为虚函数表是一个数组。 为了把对象和虚函数表关联起来,编译器会在对象中安插一个指针,指向虚函数表的起始位置。

其实虚函数与虚继承在本质上是一样的:

  1. 都是虚的
  2. 执行的方法均指向被实例化的子类对象。
  3. 在传递(被继承)的过程中,只有最后被实例化的那个对象才实现虚内容,其余传递过程中均不实例化、不复制,只是指针传递。

虚函数代码在下面

 

虚函数代替写法:组合

#include <iostream>

using namespace std;


class Goods{
	double price;

public:
	Goods(double p=123.45):price(p){
		cout << "Goods("<<p<<")"<<endl;
	}
	~Goods(){cout<<"~Goods()"<<endl;}
	double getPrice(){return price;}
};


class Camera:virtual public Goods{
public:
	void take(const char* obj){
		cout << "给" << obj << "照相" << endl;
	}
	Camera(double p):Goods(p){cout<<"Camera()"<<endl;}
	~Camera(){cout<<"~Camera()"<<endl;}
};


class MP3:virtual public Goods{
public:
	void play(const char* song){
		cout << "播放歌曲《" << song << "》" << endl;
	}
	MP3(double p):Goods(p){cout<<"MP3()"<<endl;}
	~MP3(){cout<<"~MP3()"<<endl;}
};


class Phone:virtual public Goods{
public:
	void dial(const char* no){
		cout << "给" << no << "拨打电话" << endl;
	}
	Phone(double p):Goods(p){cout<<"Phone()"<<endl;}
	~Phone(){cout<<"~Phone()"<<endl;}
};


class ModernPhone : public Phone
{//组合
	string factory;
	Goods g;
	Camera c;
	MP3 m;
public:
	void visitnet(const char* url){
		cout << "访问网址" << url << endl;
	}
	ModernPhone(const char* factory,double p)
	:c(p),m(p),Phone(p),factory(factory),g(p)
	{
        cout << "ModernPhone()" << endl;
    }
    //注意这里的写法,组合
	~ModernPhone(){cout<<"~ModernPhone()"<<endl;}
	void take(const char* obj){c.take(obj);}
	void play(const char* song){m.play(song);}
	double getPrice(){return g.getPrice();}
};


int main()
{
	ModernPhone mp("nokia",500);
	mp.dial("110");
	mp.play("北京欢迎你");
	mp.visitnet("bbs.tarena.com.cn");
	mp.take("康森林");
	cout << mp.getPrice() << endl;
}

 

类型转换的检查   dynamic_cast<子类*>(父类对象地址)

含虚函数代码

#include <iostream>
#include <string>

using namespace std;


class Person{
    string name;
    bool   gender;
    protected:
    int height;
public:
    void eat(const char* food){
        cout << name << " is eating " << food << endl; 
    }
    void sleep();
    //虚函数
    virtual void show(){
        cout << "my name is " << name << ", gender is "
        << (gender?"man , height is " : "woman , height is ") 
        << height << " . " <<         endl;
    }
    Person(const char* name, bool gender, int height):
        name(name),gender(gender),height(height){}
        const string& Name()const{return name;
    }
};


class Teacher : public Person {
    string course;
public:
    void teach(const char* class_){
        height = 999;
        cout << Name() << " teacher is teaching " <<
        course << " in class " << class_ 
        << " and height is " << height << endl; 
    }
    Teacher(const char* name, bool gender, int height, const char* course)
        :Person(name, gender, height), course(course) {
        cout << "height now is " << height << endl; 
    }
    void show(){
        cout << "rewrite" << endl; 
    }
};


int main(){
    Person a("Thomas", true, 180);
    Teacher t("Tom", false, 175, "English");
    Person* p = NULL,*q = NULL;

    //通过指针调用,因为 p q 是 Person 指针类型
    //因此调用的 Person 指针的 show 。
    //如果希望依据 p q 指向对象的类型来选择 show 函数
    //需要将父类的 show 函数变为虚函数。关键字:virtual
    //这种方法称为多态
    //只有通过指针调用父类虚函数时才会有多态
    p=&a;
    q=&t;
    p->show();
    q->show();

    //注意,将子类指针指向父类对象时会有调用问题
    //强制的类型转换检测函数
    //dynamic_cast<子类*>(父类对象地址) 检测一个对象是否为子类对象,
    //如果是返回地址,否则返回 NULL
    //可以向下,不可以向上
    //要求父类(基类)中必须有虚函数
    cout << dynamic_cast<Person*>(p)  << endl;//地址
    cout << dynamic_cast<Person*>(q)  << endl;//地址
    cout << dynamic_cast<Teacher*>(p) << endl;//null
    cout << dynamic_cast<Teacher*>(q) << endl;//地址

    getchar();
    return 0;
}

 

类型转换的检查   typeinfo

#include <iostream>
#include <typeinfo>

using namespace std;


class A{
    public:
    virtual void f(){}
};


    class B : public A{};
    class C : public B{};
    class D : public A{};


int main(){

    cout << "1  " << typeid(A).name() << endl;
    cout << "2  " << typeid(C).name() << endl;

    A *p1 = new A;
    A *p2 = new B;
    A *p3 = new C;
    A *p4 = new D;
    A *p5 = new B;

    cout << "3  " << typeid(*p1).name() << endl;
    cout << "4  " << typeid(*p2).name() << endl;
    cout << "5  " << typeid(*p3).name() << endl;
    cout << "6  " << typeid(*p4).name() << endl;
    cout << "7  " << typeid(*p5).name() << endl;

    cout << "8  " << (typeid(*p2) == typeid(*p5))    << endl;
    cout << "9  " << typeid(*p2).before(typeid(*p3)) << endl;
    cout << "0  " << (typeid(*p2) != typeid(*p4))    << endl;

    //调用拷贝函数,
    //因为该函数为私有函数,所以选择引用类型
    //该函数返回值为 const
    const type_info& t = typeid(*p3);
    cout << "1  " << t.name() << endl;

    getchar();
    return 0;
}

虚函数的练习代码 

#include <iostream>
#include <typeinfo>

using namespace std;

class USB{
public:
    virtual void plugin(){cout << "class USB  识别设备"       << endl;}
    virtual void work  (){cout << "class USB  让设备工作起来" << endl;}
    virtual void stop  (){cout << "class USB  让设备停止工作" << endl;}
};


class USBDisk : public USB{
public:
    void plugin(){cout << "class USBDisk  识别设备"       << endl;}
    void work  (){cout << "class USBDisk  让设备工作起来" << endl;}
    void stop  (){cout << "class USBDisk  让设备停止工作" << endl;}
};


class USBFengShan : public USB{
public:
    void plugin(){cout << "class USBFengShan  识别设备"       << endl;}
    void work  (){cout << "class USBFengShan  让设备工作起来" << endl;}
    void stop  (){cout << "class USBFengShan  让设备停止工作" << endl;}
};


class Computer{
public:
    void use(USB* p){p->plugin();p->work();p->stop();}
    // void use(USBDisk* p){p->plugin();p->work();p->stop();}
    // void use(USBFengShan* p){p->plugin();p->work();p->stop();}
};



int main(){
    USBDisk d;
    USBFengShan f;
    Computer c;
    c.use(&d);
    c.use(&f);

    getchar();
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值