c++primer 第14章 c++中的代码重用 知识点总结

目录

 

14.1 包含对象成员的类

 14.1.1 has-a关系

14.1.2 初始化顺序

14.2 私有继承 

 14.2.1 私有继承

 14.2.2 保护继承

14.3 多重继承

14.3.1 本节探讨公有多重继承。

14.3.2 虚基类和非虚基类

14.4 类模板

14.4.1 类模板定义

14.4.2 模板中函数的定义 

14.4.3 模板类的使用

14.4.4 深入探讨模板类 

14.4.5 模板多功能性

14.4.6 模板具体化

 14.4.9 模板类与友元

 14.4.10 模板别名


14.1 包含对象成员的类

 14.1.1 has-a关系

 被包含成员成为类的私有部分

class Student
{
private:
    string name;    //类中包含类
 ...
}

14.1.2 初始化顺序

        先初始化基类,再按在类中声明的顺序初始化。 

14.2 私有继承 

 14.2.1 私有继承

           与类包含相似

 14.2.2 保护继承

      基类中:公有成员:类内外均可直接访问

                   私有成员:类中直接访问,类外不能直接访问,通过调用公有成员函数

                   保护成员:在基类中的行为私有成员一样

     派生类:

                  public成员               private成员                              protect成员                            

公有继承     public           只能通过基类函数接口访问           protect(类内可以直接访问)

                             (基类函数接口是public,类内外均可用) 

私有继承     private         只能通过基类函数接口访问            private(类内可直接访问)

                            (基类函数接口是private,只有类内可用)

保护继承     protect      只能通过基类函数接口访问                protect(类内可直接访问)

                           (基类函数接口是protect,只有类内可用)

私有成员和保护成员的区别:在基类中无区别;区别于派生类中,private在派生类中必须通过基类的函数接口访问,protect在派生类中可直接访问。

14.3 多重继承

14.3.1 本节探讨公有多重继承。

有如下继承关系:

代码如下: 

class Worker
{
private:
    string name;
public:
    Worker(string str):name(str){}
    void worker_show(){ cout << this->name << endl;}
};

class Singer: public Worker
{
private:
    int yinyu;
public:
    Singer(string str, int x): Worker(str), yinyu(x){}
    void singer_show(){ this->worker_show(); cout << this->yinyu << endl;}
};

class Waiter: public Worker
{
private:
    double salary;
public:
    Waiter(string str, double d):Worker(str), salary(d){}
    void waiter_show(){ this->worker_show(); cout << this->salary << endl;}
};

class Singerwaiter: public Singer, public Waiter
{
private:
    int gender;
public:
    Singerwaiter() ???
    void waiter_show() ???
};

问题有两个:

1、多重继承 SingerWaiter类 的构造函数怎么写?

2、多态继承 SingerWaiter类 的某些需要访问基类的私有数据的函数怎么写?

先回答问题1,若在 SingerWaiter类 的构造函数中直接使用初始化列表调用其基类 Singer、Waiter的构造函数,则Singer、Waiter的公共部分Worker被初始化了两次,这样不行。

为了解决1的问题,引入了虚基的概念,使用关键字virtual,virtual是虚函数的关键字,二者没什么关系,像是virtual的重载,目的是避免增加新的关键字。virtual的用法如下:

class Singer: public virtual Worker    // public与virtual谁先谁后均可
{...};

class Waiter: virtual public Waiter
{...};

引入虚基类后,SingerWaiter类 的构造函数按如下方式,用初始化列表调用Singer、Waiter的构造函数时,并不会初始化其共同的虚基类Worker包含的成员,再显式的调用虚基类Worker的构造函数即可;对于非虚基类,这样做是非法的。

Singerwaiter(string str, int y, double s, int g): Worker(str), Singer(str, y), Waiter(str, s), gender(g){}

 现在回答问题2,若在SingerWaiter_show() 中直接调用 Singer_show()和Waiter_show(),则会导致虚基类Worker中的成员会输出两次,所以不能采取这种方法。改进方案是将函数拆分重新组合,择出每个类独有的成员,编写out()函数,然后根据需要将不同的out()组合成show()函数。

解决问题1,2后的完整代码如下:

class Worker                            //虚基类
{
private:
    string name;
public:
    Worker(string str):name(str){}
    void worker_out(){cout << this->name << endl;}
    void worker_show(){ this->worker_out();}
};

class Singer: virtual public Worker     //虚基直接派生类
{
private:
    int yinyu;
public:
    Singer(string str, int x): Worker(str), yinyu(x){}
    void singer_out() { cout << this->yinyu << endl;}
    void singer_show(){ this->worker_out(); this->singer_out();}
};

class Waiter: virtual public Worker     //虚基直接
{
private:
    double salary;
public:
    Waiter(string str, double d):Worker(str), salary(d){}
    void waiter_out() {cout << this->salary << endl;}
    void waiter_show(){ this->worker_out(); this->waiter_out();}
};

class Singerwaiter: public Singer, public Waiter   //多重继承
{
private:
    int gender;
public:
    Singerwaiter(string str, int y, double s, int g): Worker(str), Singer(str, y), Waiter(str, s), gender(g){}
    void singerwaiter_out(){cout << this->gender << endl;}
    void singerwaiter_show(){this->worker_out(); this->singer_out(); this->waiter_out(); this->singerwaiter_out();}
};

14.3.2 虚基类和非虚基类

1、多态继承

采用虚基类,无二义性。

继承关系如图:

代码:

class B
{
public:
    int q(){cout << 1; return 0;}
};

class C: virtual public B
{
public:
   long q(){cout << 2; return 0;}
};

class D: virtual public B
{
};

class E: public C, public D
{
};

int main()
{
    E e;
    e.q();
    return 0;
}

执行:

不采用虚基类,有二义性。

继承关系如图:

代码:

class B
{
public:
    int q(){cout << 1; return 0;}
};

class C: public B
{
};

class D: public B
{
};

class E: public C, public D
{
};

int main()
{
    E e;
    e.q();
    return 0;
}

结果:

 采用虚基类,不同派生类从同一虚基类继承而来的函数程序可以分辨为是同一函数。若不采用虚基类,不同派生类从同一基类继承而来的函数将被视为不同函数。

14.4 类模板

14.4.1 类模板定义

template <typename T>  // template <class T> 也可
class Stack
{
public:
    Stack();
    void push(T& n);
    ...
};

14.4.2 模板中函数的定义 

template <typename T>
Stack<T>::Stack()
{
     ...
}

template <typename T>
void Stack<T>::push(T& n)
{
     ...
}

14.4.3 模板类的使用

Stack<int> s;
int i=2;
s.push(2);

1-3合在一起

#include<iostream>
#include<string>
using namespace std;

template <typename T>       //类模板声明
class Stack
{
private:
    T data[100];
    int top;
public:
    Stack();
    void push(T& n);
    T pop();
    virtual ~Stack(){};
};

template <typename T>      //类函数定义
Stack<T>::Stack()
{
    top = -1;
}

template <typename T>     //类函数定义
void Stack<T>::push(T& n)
{
    data[++top] = n;
}

template <typename T>     //类函数定义
T Stack<T>::pop()
{
    return data[top--];
}

int main()
{
    Stack<int> s;          //使用模板类
    int i=2;
    s.push(2);
    std::cout << s.pop();
    return 0;
}

14.4.4 深入探讨模板类 

     不要轻易将模板类的类型T设置为指针型,如char*,会出现问题,需要修改类中函数的写法。

14.4.5 模板多功能性

       递归使用模板

Stack< Stack<int> > s;     //嵌套之间一定要有空格,不然>>造成歧义

       使用多个类型参数

template <class T1, class T2>
class Pair
{
   ...
};

       默认类型参数

template <class T1, class T2=int>
class Pair
{
   ...
};

省略T2的值,,编译器将使用int;

虽然可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值;

14.4.6 模板具体化

 1、隐式实例化对象

Stack<int> s;

2、显式实例化对象

template class Stack<int>;

3、显式具体化模板

template <> class Stack<int>;

4、部分显式具体化

template <class T1> class Pair<T1, int>;  //template后<>放的是未被具体化的参数类型

 14.4.9 模板类与友元

1、模板类的非模板友元函数(感觉没什么用)

情景:模板类中包含一个友元函数,该友元函数的参数包含该模板类,怎么办?

函数定义时,传入友元函数的参数必须是模板类的显式具体化

void reports(Hasfriend<int> a){...};     //友元函数定义
void reports(Hasfriend<double a){...};

 若传入一个模板类Hasfriend<T>,会导致函数调用时具有的模板对象如Hasfriend<int> hasin 与定义时的参数Hasfriend<T>不匹配

template <class T>                   //友元函数定义
void reports(Hasfriend<T>& a){...};  //invaild

 正确完整的写法:

#include<iostream>
#include<string>
using namespace std;

template <typename T>
class Hasfriend
{
private:
    T item;
public:
    Hasfriend(T i):item(i){};
    friend void reports(Hasfriend<T>& a);     //友元函数声明
};

void reports(Hasfriend<int>& a)              //友元函数定义
{
    cout << a.item << endl;
};

void reports(Hasfriend<double>& a)          //友元函数定义
{
    cout << a.item << endl;
}

int main()
{
    Hasfriend<int> hain(10);
    Hasfriend<double> hadou(20.0);
    reports(hain);                        //调用友元函数
    reports(hadou);
    return 0;
}

2、模板类的约束模板友元函数 

     1中方法针对模板类的每一种具体化都需要写一个友元函数对应,很麻烦。可以修改1中示例,使友元函数本身成为模板,使类的每一个具体化都获得与友元函数匹配的具体化。

 注意:在模板类中声明友元函数时,一定要显式具体化,否则会编译错误(T is private);

#include<iostream>
#include<string>
using namespace std;

 template <class A>
 void reports(A& a);

template <typename T>
class Hasfriend
{
private:
    T item;
public:
    Hasfriend(T i):item(i){}
    friend void reports<>(Hasfriend<T>& a);    //注意一定要显式具体化
};

template <class A>
void reports(A& a)                        //友元函数定义
{
    cout << a.item << endl;
}

int main()
{
    Hasfriend<int> hain(10);
    Hasfriend<double> hadou(20.0);
    reports< Hasfriend<int> >(hain);    //调用友元函数
    reports< Hasfriend<double> >(hadou);
    return 0;
}

3、模板类的非约束模板友元函数

 2是在类外部声明友元函数模板,也可以在类内部声明友元函数模板。

#include<iostream>
#include<string>
using namespace std;

template <typename T>
class Hasfriend
{
private:
    T item;
public:
    Hasfriend(T i):item(i){}
    template <class A>
    friend void reports(A& a);     //友元函数声明
};

template <class A>
void reports(A& a)                          //友元函数定义
{
    cout << a.item << endl;
}

int main()
{
    Hasfriend<int> hain(10);
    Hasfriend<double> hadou(20.0);
    reports< Hasfriend<int> >(hain);                        //调用友元函数
    reports< Hasfriend<double> >(hadou);
    return 0;
}

 14.4.10 模板别名

1、使用 typedef 声明,需要将模板类的每一个具体化模板都声明一次。

typedef std::array<double, 12> arrd;
typedef std::array<int, 12> arri;
typedef std::array<char, 12> arrc;

arrd ad;
arri ai;
arrc ac;

2、使用 using 将模板类重命名,系统会自动根据需要提供一批简化的重命名 

template <class T>
using arraytype = std::array<T, 12>;

arraytype<int> ai;
arraytype<double> ad;
arraytype<char> ac;

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值