第13章 类继承

第13章 类继承
13.1.2.构造函数:访问权限的考虑
(1)派生类不能直接访问基类的私有成员,而必须通过基类方法,派生类的构造函数必须使用基类构造函数
RatedPlayer::RatePlayer(unsigned int r, const string &fn,const string &ln,bool ht):TableTennisPlayer(fn,ln,ht)
{
	rating = r;
}
(1)	如果不调用基类构造函数,程序将使用默认的基类构造函数
RatedPlayer::RatePlayer(unsigned int r, const string &fn,const string &ln,bool ht):TableTennisPlayer()
{
	rating = r;
}
(3)
RatedPlayer::RatePlayer(unsigned int r,cosnt TableTennisPlayer &tp/*调用复制构造函数*/):TableTennisPlayer(tp)
{
	rating = r;
}
(4)创建派生类对象是,程序先调用基类构造函数,然后才调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员。 
(5)派生类的构造函数总是要调用基类的构造函数。可以用初始化列表的语法要指明要使用基类的构造函数,否则将使用默认的基类构造函数
13.2继承:is-a关系(is-a-kind-of)
Has-a  Fruit 对象作为Lunch类的数据成员
Is-like-a
Is-implemented-as-a(作为……..来实现)例如:可以用数组实现栈,但从Array类派生出Stack类是不合适的,因为栈不是数组。例如:数组的索引不是栈的属性,正确:
通过让栈包涵体格Array对象成员来隐藏数组实现
Uses-a:例如:可以使用友元函数或类处理Printer对象和Computer对象之间的通信。
13.3多态公有继承
(1)如果没有virtual,程序将根据引用类型或指针类型选择方法;如果使用virtual程序将根据引用和指针指向的对象的类型来选择方法。
(2)为什么要调用虚析构函数?
如果析构函数不是虚的,则将只调用对应于指针类型的析构函数。
如果是虚的,则将调用相应对象类型的析构函数,然后在调用基类的析构函数。
作用:使用虚析构函数可以确保正确的析构函数序列被调用
13.4静态联编和动态联编
(1)编译器对非虚方法使用静态联编
	编译器对虚方法使用动态联编
(2)	为什么默认为静态联编?P515
[1]效率:静态联编效率高
[2]概念:可能包含一些不在派生类重新定义的成员函数,不将这些函数设置为虚函数
13.4.2
(1)虚函数的工作原理:
		给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表,虚函数表中储存了为类进行声明的虚函数地址。例如:基类对象中包含了一个指针,改指针指向基类中所有虚函数的地址表
              调用虚函数时,程序将查看储存在对象vtbl地址,然后转向相应的虚数地址表。如果使用类声明中定义了的第一个虚函数,则程序将使用数组中的第一个虚数地址,并执行具有改地址的函数。
缺点:[1]每个对象都将增大,增大量为储存地址的空间
	[2]对于每个类,编译器都创建一个虚函数地址表(数组)
	[3]对于每个函数调用,都需要执行一项额外的操作,机到表中查找地址
13.4.3
(1)构造函数:不能是虚函数。创建派生类对象,将调用派生类的构造函数,而不是基类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数-------派生类要使用基类的构造函数
(2)析构函数:
	通常应该给基类提供一个虚析构函数,即使它并不需要析构函数
(3)友元:友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数
(4)重新定义将隐藏方法
Class Dwelling
{
	public :
		virtual void showperks(int a)const;
};
Class Hovel:public Dwelling
{
	Public:
		virtual void showperks()const ;
};
重新定义不会生成函数的两个重载版本,而是隐藏了接收int参数的基类版本,隐藏同名函数的基类方法,不管参数特征标如何。
两条经验:
		[1]如果重新定义继承方法,应确保与原来的原型完全相同,返回类型协变,因为返回类型随类类型的变化而变化
class Dwelling
{
	public :
		virtual Dwelling &bulid(int n);
};
class Hovel:public Dwelling
{
	Public:
 		virtual Hovel &build(int n)
};这种例外只是适用于返回值,不适用于参数
	[2]如果基类被重载了,则应该在派生类中重新定义所有的基类版本。
class Dwelling
{
	Public :
		Virtual void showperks(int a) const;
		Virtual void showperks(double x)const;
		Virtual  void showperks() cosnt;
};
Class Hovel:public Dwelling
{
	Public :
		Virtual void showperks(int a) const;
		Virtual void showperks(double x)const;
		Virtual  void showperks() cosnt;
};
如果只重新定义一个版本,则另外两个版本将被隐藏,派生类将无法使用它们。
注意:如果不需要修改它们,则重新定义只需要基类版本
Void Hovel::showperks() const
{
	Dwelling::showperks();
}
13.7继承和动态内存分配
#include<iostream>
#include<string.h>
using namespace std;

class baseDMA
{
    private:
    char *label;
    int rating;
    public :
    baseDMA(const char *l="null",int r = 0 );
    baseDMA(const baseDMA &rs);
    virtual ~baseDMA();
    baseDMA &operator =(const baseDMA &rs);
};
 baseDMA::baseDMA(const char *l,int r )
{
    label = new char[strlen(l)+1];
    strcpy(label,l);
    rating = r;
    cout<<" baseDMA::baseDMA(const char *l,int r )"<<endl;
}
baseDMA::baseDMA (const baseDMA &rs)
{
    label = new char [strlen(rs.label)+1];
    strcpy(label,rs.label);
    rating = rs.rating;
    cout<<"baseDMA::baseDMA (const baseDMA &rs)"<<endl;
}
baseDMA::~baseDMA()
{
    delete []label;
    cout<<"baseDMA::~baseDMA()"<<endl;
}
baseDMA &baseDMA::operator =(const baseDMA &rs)
{
        if(this==&rs)
    {
        cout<<"baseDMA &baseDMA::operator =(const baseDMA &rs)"<<endl;
        return *this;
    }
    delete []label;
    label = new char [strlen(rs.label)+1];
    strcpy(label,rs.label);
    rating = rs.rating;
    cout<<"baseDMA &baseDMA::operator =(const baseDMA &rs)"<<endl;
    return *this;
}
/******************************************************************************/
class lackDMA:public baseDMA
{
    private:
    char *color;
    public:
    lackDMA(const char *color)
    {
        color = new char [strlen(color)+1];
        strcpy(this->color,color);
        cout<<"lackDMA(const char color[40])"<<endl;
    }
    lackDMA(const lackDMA &hs):baseDMA(hs)/*!!!!!!!!!!!!!!*/
    {
        color = new char [strlen(hs.color)+1];
        strcpy(color,hs.color);
        cout<<"lackDMA(const hasDMA &hs):baseDMA(hs)"<<endl;
    }
    lackDMA & operator =(const lackDMA &hs)
    {
        if(this == &hs)
        {
            return *this;
        }
        baseDMA::operator=(hs);/*!!!!!!!!!!!!!!*/
        //*this = hs;
        delete []color;
        color = new char[strlen(hs.color)+1];
        strcpy(color,hs.color);
        return  *this;
    }
    virtual ~lackDMA()
    {
        delete []color;
        cout<<"virtual ~lackDMA()"<<endl;
    }

};
int main()
{
    {
        lackDMA lack1("hello world");
        cout<<"-----------------------"<<endl;
        lackDMA lack2("hello yuanyuan");
        lack2=lack1;
        cout<<"-----------------------"<<endl;
    }

    return 0;
}
13.8.2
(1)按值传递对象与传递引用
                       [1]提高效率,按值传递对象涉及到生成临时拷贝,即调用复制构造函数,然后调用析构函数。如果函数不修改对象,应将参数声明为const。
			[2]在继承使用虚函数时,被定义为接收基类引用参数的函数可以接收派生类
(2)返回对象和返回引用
测试:
#include<iostream>

using namespace std;


class A
{
    public :
    int a;
    public :
    A(int a)
    {
        this->a = a;
        cout<<"A(int a)"<<endl;
    }
    A(const A &aa)
    {
        this->a=aa.a;
        cout<<"A(const A &aa)"<<endl;
    }
    A &operator =(const A &aa)
    {
        this->a = aa.a;
        cout<<"A &operator =(const A &aa)"<<endl;
    }
    ~A()
    {
      cout<<"~A"<<endl;
    }
};

A play(const A &a)
{
    return a;
}

int main()
{
    {
        //A a(100);
        //A b(200);
        //b=play(a);
        A temp=play(5);
    }

    return 0;
}
(3)使用const
                 [1]确保方法不修改参数
		 [2]不修改调用它的对象
		  [3]可以使用const来确保引用和指针返回的值不能用于修改对象中的数据
		[4]P539
13.8.3
什么不能被继承?
(1)	构造函数:创建派生类对象时,必须调用派生类的构造函数
(2)	析构函数:在释放对象时,程序将首先调用派生类的析构函数,然后调用基类的析构函数。如果基类有默认析构函数,编译器将为派生类生成默认析构函数
(3)	赋值运算符:因为它包含一个类型为其所属类的形参。
可以通过强制类型转换将,派生类的引用或指针转换为基类引用或指针,然后使用转换后的指针或引用调用基类的友元函数:
ostream &operator<<(operator &os,const  hasDMA &hs)
{
	os<< (const baseDMA&)hs;
	os<<”Style: “<<hs.style<<endl;
	return os;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值