转换和继承,虚函数

Understanding conversions between base and derived classes is essential to

understanding how object-oriented programming works in C++.

理解基类和派生类之间的转换是不可缺少的 理解面向对象编程在。





Like built-in pointers, the smart pointer classes (§12.1, p. 450) support the

derived-to-base conversion—we can store a pointer to a derived object in a

smart pointer to the base type.





像内置指针,智能指针类支持 导出到基类的转换能够存储一个指向派生类对象的一个

基类类型的指针。

Static Type and Dynamic Type(静态和动态类型)

class Quote
{
public:
Quote()=default;
Quote(const string &book, double sales_price):bookNo(book), price(sales_price)
{cout<<"Quote gouzhao function"<<endl;}
string isbn() const {return bookNo;}
//返回指定数量的项目总销售价格
//派生类将重写应用不同的折扣算法
virtual double net_price(size_t n) const {return n*price;}
virtual ~Quote()=default; //动态链接析构函数
private:
string bookNo; //这本书的isbn号
protected:
double price=0.0; //不打折的价格 }; //继承,怎样继承?
class Bulk_quote : public Quote
{
public:
Bulk_quote()=default;
Bulk_quote(const string & book, double p, size_t qty, double disc)
: Quote(book, p), min_qty(qty), discount(disc)
{cout<<"Bulk_quote construct function"<<endl;}
//重写虚函数
double net_price(size_t n) const override {cout<<"double net_price(size_t)"<<endl; return n*price;}
//再次说明,请声明函数后一定要记得定义它。不然我这是出了各种莫名其妙的错误!
// ~Bulk_quote2(){cout<<"~Bulk_quote2()"<<endl;}
private:
size_t min_qty=0;
double discount=0.0;
};

There Is No Implicit Conversion from Base to Derived ...

void fun1()
{
Quote base;
// Bulk_quote* bulkP=&base; 错误不能把基类转换成派生类
// Bulk_quote& bulkRef=base; 同上
Bulk_quote bulk;
Quote *itemP=&bulk; //派生类转换成基类
// Bulk_quote *bulkP=itemP; error:基类到派生类
}

...and No Conversion between Objects

void fun2()
{
Bulk_quote bulk; //派生类
Quote item(bulk); //调用基类Quote的赋值构造函数
item=bulk; // calls Quote::operator=(const Quote&)拷贝赋值运算符
}

这里当我们的參数对象是基类的时候。转换的时候仅仅有基类部分会被拷贝。而派生的那部分

会直接被忽略


Virtual Functions虚函数

Key Concept: Conversions among Types Related by Inheritance

There are three things that are important to understand about conversions

among classes related by inheritance:

• The conversion from derived to base applies only to pointer or reference

types.

• There is no implicit conversion from the base-class type to the derived

type.

• Like any member, the derived-to-base conversion may be inaccessible due

to access controls.

关键概念:继承关系的类型之间的转换

因继承而相关联的类中转换的重要的三件事 :

•从派生类到基类的转换仅仅适用于指针或引用 类型。

•有从基类到派生类没有隐式转换

•像不论什么成员,派生类到基类的转换可能无法訪问因为 訪问控制。


动态绑定

double print_total(ostream &os, const Quote &item, size_t n)
{
//依据不同的对象来绑定到这个參数的类型
//这里引用Quote::net_price 或 Bulk_quote::net_price
double ret=item.net_price(n);
os<<"ISBN: "<<item.isbn() //调用 Quote::isbn
<<" # sold: "<<n<<" total due: "<<ret<<endl; return ret; //这里上面的Quote參数是能够接受Quote或者Bulk_quote类型的
}

Calls to Virtual Functions May Be Resolved at Run Time

调用的虚函数能够在执行时确定

void fun3()
{
Quote base("0-201-82470-1", 50);
print_total(cout, base, 10); //调用Quote的net_price
Bulk_quote derived("0-201-82470-1", 50, 5, 0.19);
print_total(cout, derived, 10); //调用Bulk_quote的net_price
base=derived; //吧quote类型的部分复制到base
base.net_price(20);
}

Virtual Functions in a Derived Class(【派生类中的虚函数)

A function that is virtual in a base class is implicitly virtual in its

derived classes. When a derived class overrides a virtual, the parameters in

the base and derived classes must match exactly.

The final and override Specifiers

(1)       重载的几个函数必须在同一个类中。

覆盖的函数必须在有继承关系的不同的类中

(2)       覆盖的几个函数必须函数名、參数、返回值都同样;

重载的函数必须函数名同样,參数不同。參数不同的目的就是为了在函数调用的时候编译器可以通过參数来推断程序是在调用的哪个函数。

这也就非常自然地解释了为什么函数不能通过返回值不同来重载。由于程序在调用函数时非常有可能不关心返回值,编译器就无法从代码中看出程序在调用的是哪个函数了。

(3)       覆盖的函数前必须加keywordVirtual;

重载和Virtual没有不论什么瓜葛,加不加都不影响重载的运作。


struct B
{
virtual void f1(int) const;
virtual void f2();
void f3();
}; struct D1 : B //这是什么继承?
{
void f1(int) const override; //ok能够覆盖
// void f2(int) override; error没有f2(int)这个虚函数
// void f3() override; //error:f3()不是虚函数
// void f4() override; error:没有f4()这个虚函数
};

final这个keyword

struct D2 : B
{
//从B继承f2,f3之后我们override f1
void f1(int) const final; //之后派生类无法覆盖f1
}; struct D3 : D2
{
void f2();
// void f1(int) const; //注意:这个函数是被final修饰的函数
};

Virtual Functions and Default Arguments(虚函数和默认參数)

具有默认參数的虚函数应该使用同样的參数 在基值和派生类。


Circumventing the Virtual Mechanism

void fun4()
{
cout<<"there is fun4"<<endl;
Quote *baseP; double undiscounted = baseP->Quote::net_price(42);
}

Ordinarily, only code inside member functions (or friends) should need to use

the scope operator to circumvent the virtual mechanism

通常仅仅有类里面的成员函数(友元函数)须要使用作用域操作符来规避虚拟机制


15.4. Abstract Base Classes

抽象类就是类里定义了纯虚成员函数的类





为什么要定义抽象基类呢?依我所见主要有下面原因:

1.最重要的原因是,能够将接口与实现分离。

接口是软件产品最有价值的资源。

设计接口比实现接口须要耗费更昂贵的成本。

因此,要将接口保护起来,

以免在针对客户需求改动实现的时候,程序猿不小心把接口破坏掉。

2.引入抽象基类和纯虚函数方便实现C++的多态特性。

能够用抽象基类的指针去调用子类对象的方法。

3.非常多时候。很多基类被实例化是不合理的。

比如“形状”这个基类,被实例化之后反而会让人相当费解,

所以干脆将“形状”这个类定义为抽象类,由它派生出正方形,三角形等子类。


纯虚函数

当类声明中包括纯虚函数时。则不能创建该类的对象。

基类的纯虚函数必须有“=0”,但不一定没有函数的实现,仅仅是不能直接内嵌在类中。


class Disc_quote : public Quote
{
public:
Disc_quote()=default;
Disc_quote(const string & book, double price, size_t qty, double disc):
Quote(book, price), quantity(qty), discount(disc) {cout<<"Disc_quote构造函数"<<endl;}
double net_price(size_t) const = 0; //纯虚函数
protected:
size_t quantity=0;
double discount=0.0;
};

纯虚函数不能直接在类里面进行定义。要定义就要在外面

virtual void Move(int nx, int ny) = 0;

void BaseEllipse::Move(int nx, int ny) {x = nx; y = ny;}

这样是同意的


含有纯虚函数的类就是抽象类

void fun5()
{
cout<<"there is fun5"<<endl;
// Disc_quote discount; //error:Disc_quote是一个抽象类,含有纯虚函数没法实例化
Bulk_quote bulk; //ok,这个里面没有纯虚函数,不是抽象类
}

A Derived Class Constructor Initializes Its Direct Base Class Only

就是派生类參数列表能够直接初始化基类

class Bulk_quote2 : public Disc_quote
{
public:
Bulk_quote2()=default;
//直接初始化
Bulk_quote2(const string& book, double price, size_t qty, double disc):
Disc_quote(book, price, qty, disc) {}
double net_price(size_t) const override; //覆盖纯虚函数
};




有些人认为,做人要真,所以说话要直。结果就到处直来直去得罪人。事实上大错。中国人写"真"字,是"直"以下两点。也就是说,一些实话、直话,也要保留两点。不要所有说出去。实话实说是真,但实话全说就是蠢。肚子里藏不住话的人,自以为说真话。事实上只是是情商不够而已。

不幸的是,作者好像既不真也不直~~~~~

版权声明:本文博客原创文章,博客,未经同意,不得转载。

【足迹C++primer】52、,转换和继承虚函数的更多相关文章

  1. C++ 由虚基类 虚继承 虚函数 到 虚函数表

    //虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...

  2. 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。

    本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...

  3. (C/C++学习)5.C++中的虚继承-虚函数-多态解析

    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...

  4. c++ 继承 虚函数与多态性 重载 覆盖 隐藏

    http://blog.csdn.net/lushujun2011/article/details/6827555 2011.9.27 1) 定义一个对象时,就调用了构造函数.如果一个类中没有定义任何 ...

  5. C++基础 (6) 第六天 继承 虚函数 虚继承 多态 虚函数

    继承是一种耦合度很强的关系 和父类代码很多都重复的 2 继承的概念 3 继承的概念和推演 语法: class 派生类:访问修饰符 基类 代码: … … 4 继承方式与访问控制权限 相对的说法: 爹派生 ...

  6. 一道关于C++ 继承/虚函数 笔试题 [转]

    转自:http://www.cnblogs.com/yangyh/archive/2011/06/04/2072393.html 首先这位作者, 因为看了这篇简短的一个博文, 我相同了关于虚函数方面的 ...

  7. 【C++基础之十一】虚函数的用法

    虚函数的作用和意义,就不进行说明了,这里主要讨论下虚函数的用法. 1.典型的虚函数用法 可以看到,只有标识为virtual的函数才会产生多态的效果,而且是编译多态.它只能借助指针或者引用来达到多态的效 ...

  8. c++ virturn function -- 虚函数

    c++ virturn function -- 虚函数 pure irtual function  -- 纯虚函数   先看例子 #include <iostream> using nam ...

  9. 113.dynamic_cast 虚函数 通过子类初始化的父类转化为子类类型

    #include <iostream> using namespace std; //子类同名函数覆盖父类 //父类指针存储子类地址,在有虚函数情况会调用子类方法,否则会调用父类方法 cl ...

随机推荐

  1. 制作openstack用的centos6.5镜像

    目的: 在centos6.5操作系统环境下制作一个centos6.5的kvm镜像,安装cloud-init,能自己主动扩展根分区 一.制作环境: 操作环境是在openstack平台开一个实例.装的是c ...

  2. pc2日记——有惊无险的第二天2014/08/29

    今天下午如期的用pc2进行了第二场比赛.因为昨天的出错经历和早上充足的准备,下午的比赛尽管在開始的时候出了点小小的问题,但总的来说还是非常成功的. 早上八点过去504開始又一次配置client,由于开 ...

  3. Hibernate(五)——经典解析一对一关联映射

    前面两篇介绍了多对一.一对多的映射.今天分享下一对一的关联映射关系.有两种策略可以实现一对一的关联映射:主键关联.唯一外键关联. 主键关联——两个表有完全相同的主键值,来表示它们的一对一的关系.数据库 ...

  4. 利用PHP SOAP扩展实现简单Web Services

    原文:利用PHP SOAP扩展实现简单Web Services WebServices能干什么? WebServices 可以将应用程序转换为网络应用程序. 通过使用 WebServices,您的应用 ...

  5. 【Windows Phone设计与用户体验】关于移动产品的Loading用户体验的思考

    作为一款运行在移动端上的产品,必定会有一些耗时的操作.为了具有良好的用户体验,Loading效果是必不可少的,而什么形式的Loading才会有良好的用户体验? Loading形式简单分为两类: 一.遮 ...

  6. java垃圾回收那点事(二)不同gc策略的heap分配

    在前面的文章中曾提到了在java虚拟机启动的时候会对G1,CMS, SerialGC定义不同的heap的类,并且定义不同的policy. CollectorPolicy CollectorPolicy ...

  7. 安卓开发28:自定义View类

    自定义View类 通过自定义View类,可以自定义复杂的,按照自己需求的控件. 一个简单的例子 mainActivity.java 这个里面就是最普通的代码,但是给自定义的控件加上了一个onclick ...

  8. c++中sort()及qsort()的使用方法总结

    当并算法具体解释请见点我 想起来自己天天排序排序,冒泡啊,二分查找啊,结果在STL中就自带了排序函数sort,qsort,总算把自己解脱了~ 所以自己总结了一下,首先看sort函数见下表:   函数名 ...

  9. AW笔记本升级SSD,外接双屏中的一些注意事项

    自己留一个mark,以后提醒用. 1)机械硬盘状态下利用alien sprawn创建的系统恢复U盘,无法在SSD下使用,由于SSD中没有recovery分区,仅仅能使用随机携带的系统恢复光盘: 2)最 ...

  10. Exception in thread &quot;main&quot; java.lang.IllegalArgumentException

    1.错误叙述性说明 Exception in thread "main" java.lang.IllegalArgumentException: Cannot format giv ...