第十次复习

c++ primer plus 14章

1.有关valarry类(stl模块中会提到这里不做说明)
因为这本书穿插着讲知识,但是我一贯秉持着一丝不苟的态度,所以不放过每一个记下来的知识点
2.has-a关系
与is-a关系相对立
比喻:学生和笔,课本的关系,笔和课本作为对象在学生类中
3.私有继承
将基类的公有方法变为派生类的私有方法,将基类的公有保护成员变为派生类的私有成员

百度百科上对于私有继承的解释:
私有继承后,基类所有成员在派生类中为private成员。看似没有任何意义,但私有继承可以帮助我们复用基类代码,并防止基类接口的曝光。

百度上引用来的代码

#include <iostream>
  using std::cout;
  using std::endl;
class engine {
  public :
  void start() {cout << "engine->start" << endl;}
  void move() {cout << "engine->move" << endl;}
  void stop() {cout << "engine->stop" << endl;}
  };
class wheel {
  public :
  void start() {cout << "wheel->start" << endl;}
  void move() {cout << "wheel->move" << endl;}
  void stop() {cout << "wheel->stop" << endl;}
  };
class car : private engine, private wheel {
  public :
  void start();
  void move();
  void stop();
  };
void car::start() {
  engine::start();
  wheel::start();
  }
void car::move() {
  engine::move();
  wheel::move();
  }
void car::stop() {
  engine::stop();  //域运算符访问
  wheel::stop();
  }
int main(int argc, char* argv[]) {
  car ca;
  ca.start();
  ca.move();
  ca.stop();
  return 0;
  }

简单的来说,就是只能派生类通过域运算符来使用基类公有方法,而基类私有成员会永久性无法访问

4.私有继承中的虚函数

某博主结论:

  1. 私有继承的虚函数, 其访问权限可由派生类指定.
  2. 私有继承后, 无法在派生类作用域外进行多态, 派生类指针无法自动转换为基类指针(但可强制类型转换).
  3. 当派生类将基类中public权限的虚函数重载并限制为private或protected时, 多态时直接使用基类指针将忽略派生类对该虚函数访问权限的更改,以基类函数的访问权限为依据调用该虚函数.

某评论的结论:

我分析原因是可能是这样的:
1、私有继承产生的派生类,将不再与基类具有相同的接口,多态的条件是派生类与基类具有相同的接口,因此私有继承类不具有多态的性能。
2、私有继承类的虚拟函数表vptr将改写基类继承过来的vptr表,如本例中Base中的VirtualFun函数在vptr中的值(Base::VirtualFun函数地址),在派生类中将被Child::VirtualFun函数地址覆盖,当使用基类指针访问虚拟函数时(前提条件是基类可以访问虚拟函数),将直接读取vptr虚拟函数表中对应的地址进行函数调用。
3、派生类对于基类函数的private及public属性都是继承下来的,但派生类中重命名一个与基类相同的函数时,可以认为派生类中的函数将重载覆盖基类中继承的函数(在C++的书籍中都有介绍派生类重定义基类同名函数的要点),因此访问性由派生类决定。一个基类A(设有成员函数A)进行一次私有派生B再进行一次公有派生C,在C中改写A,并设置为public,那么C的实例将仍旧可以访问A(访问的是C::A)。

总之就是说,虚方法如果在私有继承中,要么被派生类重新重定义,要么有其他公有派生类来使用,不然根本无法实现多态

注意点

保护继承:关键字protect:
1、保护继承之后基类的公有成员到派生类里变成保护成员,到第三代仍然可以使用他们
2、protected 继承将基类中 public 成员变为子类的 protected 成员,其它成员的访问权限不变
3.隐式向上转换只有在公有继承和保护继承时可用,在私有继承时不可用
提醒:向上转换=派生-》基类 。向下转换则反之

5.using方法突破访问权限设置
笔记上记到过,试了半天没搞明白,因为using 方法作用于命名空间或者打包过的自定义命名空间
用法:using valarry <double 》::max ; //仅继承

6.多重继承(MI)
基本形式:
A: B ; A:C /// D: B,C ;
先把笔记搬上来:
class a : virtual public b ;
class c: virtural public b ;
class d: public a , c ;
//说明:a,c共享继承一个b , d继承的只有b (virtual重载)

先说一下多重继承的问题
1,C++ 支持多重继承的编程方式:

除了 C++ 支持多重继承,基本上其它语言并不支持;

2 .多重继承容易带来问题:

   2.1可能出现“同一个对象的地址不同”的情况;

   2.2虚继承可以解决数据冗余的问题;

   2.3虚继承使得架构设计可能出现问题;

演示代码

#include <iostream>  
using namespace std;

class Vehicle
{
public:
    Vehicle(int weight = 0)
    {
        Vehicle::weight = weight;
        cout << "载入Vehicle类构造函数" << endl;
    }
    void SetWeight(int weight)
    {
        cout << "重新设置重量" << endl;
        Vehicle::weight = weight;
    }
    virtual void ShowMe() = 0; //定义纯虚函数
protected:
    int weight;
};
class Car :virtual public Vehicle//汽车,这里是虚拟继承  
{
public:
    Car(int weight = 0, int aird = 0) :Vehicle(weight)
    {
        Car::aird = aird;
        cout << "载入Car类构造函数" << endl;
    }
    void ShowMe() //定义虚函数
    {
        cout << "我是汽车!" << endl;
    }
protected:
    int aird;
};

class Boat :virtual public Vehicle//船,这里是虚拟继承  
{
public:
    Boat(int weight = 0, float tonnage = 0) :Vehicle(weight)
    {
        Boat::tonnage = tonnage;
        cout << "载入Boat类构造函数" << endl;
    }
    void ShowMe() //定义虚函数
    {
        cout << "我是船!" << endl;
    }
protected:
    float tonnage;
};

class AmphibianCar :public Car, public Boat//水陆两用汽车,多重继承的体现  
{
public:
    AmphibianCar(int weight, int aird, float tonnage)
        :Vehicle(weight), Car(weight, aird), Boat(weight, tonnage)
        //多重继承要注意调用基类构造函数,一定要完整赋值  
    {
        cout << "载入AmphibianCar类构造函数" << endl;
    }
    void ShowMe() //再次定义虚函数
    {
        cout << "我是水陆两用汽车!" << endl;
    }
    void ShowMembers()
    {
        cout << "重量:" << weight << "顿," << "空气排量:" << aird << "CC," << "排水量:" << tonnage << "顿" << endl;
    }
};
int main()
{
    AmphibianCar a(4, 200, 1.35f);
    a.ShowMe();
    a.ShowMembers();
    a.SetWeight(3);
    a.ShowMembers();
    return 0 ; 
}

虚继承的主要体现是在两个类继承了基类的setweight后, AmphibianCar 继承两个虚派生类后,自动忽略了两个相同版本的来自基类的setwight方法!

class ZooAnimal
{

};

class Bear : public ZooAnimal
{
public:
    void print(int x) { cout << "print Bear" << endl; }

};

class Endangered
{
public:
    void print() { cout << "print Endangered" << endl; };
};

class Panda : public Bear, public Endangered
{
};

int main()
{
    Panda p;
    p.Bear::print(1);  //产生二义  p.print(1);
    p.Endangered::print();
    return 0;
}

//用域名运算符强制指定调用可以避免冲突

7.多重继承的补充要点
@1 多重所有构造函数都要赋值 , 是一层层递进的关系,根据已有的虚构或者类的规则进行赋值或操作
@2 函数外使用或异名函数中引用其他对象变量或函数一定要用域运算符
@3 另外,在自己定义函数种可以直接调用对象函数(以关系为基础)
@4 二义性,派生类中的名称优于直接或间接祖先的中的相同名称,如果派生类中没有或者直接重名会造成二义性

8.模板类

#include<iostream>
using namespace std ;
template <class t >
class nums 
{
    t a ;
public :
    nums( t k ) : a(k) {};
    t count ( ) ;

};
template <class t > 
t nums<t>::count ()
{
    return a*a ;
}
int main()
{
    nums<double> l = 3.6; //c++11初始化列表法
    int c = l.count() ;
    cout << c <<endl;
return 0 ;
}

就是有几个需要注意的地方
@1 注意定义函数时都要在之前加上模板的声明
@2 注意定义函数时的写法nums《》!
@3根据可能需要的数据类型来定义函数

9.深入探讨模板类
1.模板template<class t , int n 》

#include<iostream>
using namespace std ;
template <class t ,int n >
class nums 
{
    t a ;
public :
    nums( t k ) : a(k) {};
    t count ( ) ;

};
template <class t, int n > 
t nums<t,n>::count ()
{
    return a*a*n ;
}
int main()
{
    nums<double,10> l = nums<double , 10>(3.6) ; //注意调用方式
    int c = l.count() ;
    cout << c <<endl;
return 0 ;
}

模板参数只能为整型枚举引用或者指针!!

2.使用带参数的表达式定义模板时使用两次会生成两个独立的类声明,而不使用不会
3.模板的嵌套性

Arrary<stack<int>> ; //解释,stack把int作为模板参数//而array 把类作为模板参数
Array<Array<int,5>,10> ;  //这里是递归

下面一种情况我详细讲一下,由于第一个参数是可变模板参数,所以最里面的定义代表把类型定义成int和5参数的类,然后往外递进(函数里有定义第一个模板参数的两个版本,一个接受数据类型进行初始化,另一个接受类进行递归)这时候把基层定义好的类给类中接受类参数的模板版本,第二个参数采用10进行无限递归。

10.显示实例化和具体化和默认参数细节

#include<iostream>
using namespace std ;
template <class t ,class g = int  > //这里给了默认值,如果不指定则为默认
class nums 
{
    t a ;
    g b ;
public :
    nums( t k , g m  ) : a(k), b(m) {};
    t count ( ) ;

};
template <class t, class g  >  //这里不要再次给默认值,否则报错
t nums<t,g>::count ()
{
    return a*b ;
}
int main()
{
    nnums<double,double > l = {3.6 , 2.5 } ; //显示定义
    //nnums<double > l = {3.6  } ; //使用默认类型 
    double c = l.count() ;
    cout << c <<endl;
return 0 ;
}

具体化和实例化

#include<iostream>
using namespace std ;
template <class t ,class g   >
class nums 
{
    t a ;
    g b ;
public :
    nums( t k , g m  ) : a(k), b(m) {};
    t count ( ) ;

};
template <class t, class g  > 
t nums<t,g>::count ()
{
    return a*b ;
}
template class nums<double,double > ; //显示实例化
template<> class nums <int, int >  //显示具体化
{
    int k ; 
    
};
int main()
{
    nums<double ,double>*ptr ;
   // double c = l.count() ;
   // cout << c <<endl;
return 0 ;
}

显式实例化:
当显式实例化模板时,在使用模板之前,编译器根据显式实例化指定的类型生成模板实例。如前面显示实例化(explicit instantiation)模板函数和模板类。其格式为:
template typename function(argulist);
template class classname;
显式实例化只需声明,不需要重新定义。编译器根据模板实现实例声明和实例定义。

显示具体化:
对于某些特殊类型,可能不适合模板实现,需要重新定义实现,此时可以使用显示具体化(explicite specialization)。显示实例化需重新定义。格式为:
template<> typename function(argu_list){…};
template<> class classname{…};

看到一篇很详细的文章,还有函数的显式实例化和具体化
详解显示实例化和具体化的文章
可以重载
同名非模板有限度大于模板

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值