参考:http://www.weixueyuan.net/view/6363.html

总结:

  在codingbook类中新增了一个language成员变量,为此必须重新设计新的构造函数。在本例中book类中有一个默认构造函数和一个带参数的构造函数,codingbook类中同样声明了两个构造函数,一个默认构造函数和一个带参数的构造函数,默认构造函数显式调用基类的默认构造函数,带参构造函数显式调用基类的带参构造函数。  

  codingbook():book(){lang = none;}  //定义派生类的构造函数时 显示调用 基类的构造函数
  codingbook::codingbook(language lang, char * t, double p):book(t,p)

  如果继承关系有好几层的话,例如A类派生出B类,B类派生出C类,则创建C类对象时,构造函数的执行顺序则为A的构造函数,其次是B的构造函数,最后是C类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。

在前一章节中,我们介绍了构造函数的功能和用法,派生类同样有构造函数。当我们创建一个派生类对象的时候,基类构造函数将会被自动调用,用于初始化派生类从基类中继承过来的成员变量。而派生类中新增的成员变量则需要重新定义构造函数用于初始化了。

例1:

#include<iostream>
using namespace std; class book
{
public:
book();
book(char* a, double p = 5.0);
void setprice(double a);
double getprice()const;
void settitle(char* a);
char * gettitle()const;
void display();
private:
double price;
char * title;
}; class book_derived :public book
{
public:
void display();
}; book::book(char* a, double p)
{
title = a;
price = p;
} book::book()
{
title = "NoTitle";
price = 0.0;
} void book::setprice(double a)
{
price = a;
} double book::getprice()const
{
return price;
} void book::settitle(char* a)
{
title = a;
} char * book::gettitle()const
{
return title;
} void book::display()
{
cout<<"The price of "<<title<<" is $"<<price<<endl;
} void book_derived::display()
{
cout<<"The price of "<<gettitle()<<" is $"<<getprice()<<endl;
} int main()
{
book_derived b;
b.display();
return ;
}

在本例中定义了book_derived类,该类没有自身的成员变量,类中所有成员变量都继承自book类,类中成员函数仅有一个display函数,该函数遮蔽了基类book中的display函数。在主函数中定义派生类的对象b,之后调用派生类的display函数,程序运行结果为:“The price of NoTitle is $0”。

从这例1中,我们不难看出派生类在创建对象时会自动调用基类构造函数。如果像例1这种情况,派生类中没有新增成员变量,基类的构造函数功能已经满足派生类创建对象初始化需要,则派生类则无需重新自定义一个构造函数,直接调用基类构造函数即可。如果派生类中新增了成员变量,这时如果需要在创建对象时就进行初始化则需要自己设计一个构造函数,具体见例2。

例2:

#include<iostream>
using namespace std; enum language{none, cpp, java, python, javascript, php, ruby}; class book
{
public:
book();
book(char* a, double p = 5.0);
void setprice(double a);
double getprice()const;
void settitle(char* a);
char * gettitle()const;
void display();
private:
double price;
char * title;
}; class codingbook: public book
{
public :
codingbook():book(){lang = none;}
codingbook(language lang, char * t, double p);
void setlang(language lang);
language getlang(){return lang;}
void display();
private:
language lang;
}; book::book(char* a, double p)
{
title = a;
price = p;
} book::book()
{
title = "NoTitle";
price = 0.0;
} void book::setprice(double a)
{
price = a;
} double book::getprice()const
{
return price;
} void book::settitle(char* a)
{
title = a;
} char * book::gettitle()const
{
return title;
} void book::display()
{
cout<<"The price of "<<title<<" is $"<<price<<endl;
} void codingbook::setlang(language lang)
{
this->lang = lang;
} codingbook::codingbook(language lang, char * t, double p):book(t,p)
{
this->lang = lang;
} void codingbook::display()
{
book::display();
cout<<"The language is "<<lang<<endl;
} int main()
{
codingbook cpp;
cpp.display();
codingbook java(java, "Thinking in Java", 59.9);
java.display();
return ;
}

本例中定义了两个类book类和codingbook类,codingbook类是book类的派生类。在codingbook类中新增了一个language成员变量,为此必须重新设计新的构造函数。在本例中book类中有一个默认构造函数和一个带参数的构造函数,codingbook类中同样声明了两个构造函数,一个默认构造函数和一个带参数的构造函数,默认构造函数显式调用基类的默认构造函数,带参构造函数显式调用基类的带参构造函数。在主函数中定义了codingbook类的对象cpp,该对象调用codingbook类的默认构造函数,codingbook类中的默认构造函数先会调用基类的默认构造函数将title和price进行初始化,之后才会执行自身函数体中的内容。之后又定义了codingbook类对象java,该对象在定义时后面接有三个参数,很明显是需要调用codingbook类的带参构造函数,其中java参数用于初始化lang成员变量,而后两个参数则用于初始化从基类继承过来的title和price两个成员变量,当然初始化顺序依然是先调用基类的带参构造函数初始化title和price,然后再执行自身函数体中的初始化代码初始化lang成员变量。

最后程序运行结果如下:
The price of NoTitle is $0
The language is 0
The price of Thinking in Java is $59.9
The language is 2

在这个例子中language没有显示为java或者cpp,只显示为0和2,这个熟悉枚举类型的应该都清楚,枚举类型在本例中其实就是从0开始的int类型。

从例2中,我们可以很清楚的看到,当我们创建派生类对象时,先由派生类构造函数调用基类构造函数,然后再执行派生类构造函数函数体中的内容,也就是说先执行基类构造函数,然后再去执行派生类构造函数。如果继承关系有好几层的话,例如A类派生出B类,B类派生出C类,则创建C类对象时,构造函数的执行顺序则为A的构造函数,其次是B的构造函数,最后是C类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。

例3:

#include<iostream>
using namespace std; class base
{
public:
base(){x = ; y = ; cout<<"base default constructor"<<endl;}
base(int a, int b){x = a; y = b; cout<<"base constructor"<<endl;}
private:
int x;
int y;
}; class derived: public base
{
public:
derived():base(){z = ; cout<<"derived default constructor"<<endl;}
derived(int a, int b, int c):base(a,b){z = c; cout<<"derived constructor"<<endl;}
private:
int z;
}; int main()
{
derived A;
derived B(,,);
return ;
}

本例中定义了两个类,基类base中定义了一个默认构造函数和一个带参数的构造函数。派生类derived中同样定义了两个构造函数,这两个构造函数一个为默认构造函数,一个为带参构造函数。派生类中的默认构造函数显式调用基类默认构造函数,带参构造函数显式调用基类的带参构造函数。我们在主函数中定义了派生类的两个对象,这两个对象一个是调用派生类的默认构造函数,另一个调用派生类的带参构造函数。

这个程序运行结果如下:
base default constructor
derived default constructor
base constructor
derived constructor

从运行结果可以看出创建对象时先是执行基类的构造函数,然后再是执行拍摄呢类构造函数。构造函数执行顺序是按照继承顺序自顶向下执行。

3.6 C++继承机制下的构造函数的更多相关文章

  1. c++ 16 this 和 继承 及继承机制中的构造函数 与 析构函数

    #include <iostream> #include <string> using namespace std; class Animal { public: Animal ...

  2. 3.8 C++继承机制下的析构函数

    参考:http://www.weixueyuan.net/view/6365.html 总结: 构造函数的执行顺序是按照继承顺序自顶向下的,从基类到派生类,而析构函数的执行顺序是按照继承顺序自下向上, ...

  3. Javascript 构造函数原型继承机制

    我们先聊聊Js的历史,1994年Netscape公司发布了Navigator浏览器0.9班.这是历史上第一个比较成熟的网络浏览器.轰动一时.但是,这个版本的浏览器只能用来浏览,不具备交互功能,最主要的 ...

  4. js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法。

    js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法. function ClassA(sColor) { this.color = sColor; } Class ...

  5. js一种继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法。

    js一种继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法. function ClassA(sColor) { this.color = sColor; } ClassA ...

  6. Javascript继承机制总结 [转]

    转自:http://bbs.csdn.net/topics/260051906 Javascript继承 一直想对Javascript再次做一些总结,正好最近自己写了一个小型Js UI库,总结了一下J ...

  7. javascript继承机制 & call apply使用说明

    一.继承机制 1.对象冒充:构造函数使用 this 关键字给所有属性和方法赋值,可使 ClassA 构造函数成为 ClassB 的方法,然后调用它. function ClassZ() { this. ...

  8. JavaScript中继承机制的模仿实现

    首先,我们用一个经典例子来简单阐述一下ECMAScript中的继承机制. 在几何学上,实质上几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形. ...

  9. 深入浅出理解Javascript原型概念以及继承机制(转)

    在Javascript语言中,原型是一个经常被讨论到但是有非常让初学者不解的概念.那么,到底该怎么去给原型定义呢?不急,在了解是什么之前,我们不妨先来看下为什么. Javascript最开始是网景公司 ...

随机推荐

  1. php爬虫最最最最简单教程

    php爬虫最最最最简单教程 一.总结 一句话总结:用的爬虫框架,却是用的自己的例子(因为网站结构的变化,作者的例子不一定好用) 爬虫框架 自己例子 1.发现自己的运行效果和作者的不一样怎么办? 耐下性 ...

  2. computational biology | Bioinformatician | 开发者指南

    对自己的定位要明确,不要定义为码农,我是computational biologist. 入了这一行就不要三心二意,这基本注定你未来10年都在干这个,就算要转行也要先把这个做好.其实大多数人最喜欢的肯 ...

  3. zookeeper在搭建的时候,解决后台启动为standalone模式问题

    今天在搭建zookeeper,搭建完成之后,启动一直报错: 上网查了好多资料:有几种解决方案: 1.在配置文件conf目录下,将zoo_sample.cfg删除,只留zoo.cfg(然而就我的情况而言 ...

  4. java.lang.NoClassDefFoundError: org/hibernate/cfg/Configuration

    java.lang.NoClassDefFoundError: org/hibernate/cfg/Configuration 最近在做项目的时候遇到了这个问题,很是困扰,多次尝试后发现是jar包的问 ...

  5. Linq to XML 增删改查

    Linq to XML同样是对原C#访问XML文件的方法的封装,简化了用xpath进行xml的查询以及增加,修改,删除xml元素的操作.C#访问XML文件的常用类:XmlDocument,XmlEle ...

  6. gcc -02引起内存溢出'unsigned i'应修订为'volatile unsigned i'

    2017-12-13 10:44:19gcc -02引起内存溢出'unsigned i'应修订为'volatile unsigned i' 1.3.100 driver/char/random.cst ...

  7. yield生成器

    def say_hi(): while True: print("before") ret0 = "返回值" msg = yield ret0 #yield后的 ...

  8. MVC实战之排球计分(一)—— 需求分析与数据库设计

    此系列博客目的是制作一款排球计分程序.这系列博客讲讲述此软件的 各个功能的设计与实现. 一.需求分析: 这个程序是排球计分程序,其业务非常简单,具体如下: 1.本程序可以选择用户身份,通过不同角度记录 ...

  9. 【IDEA】【1】准备工作

    1,官方网址(https://www.jetbrains.com/idea/) 2,网盘地址(为ideaIU-2018.2.5) 链接:https://pan.baidu.com/s/1oLgY_Pb ...

  10. 借助python工具从word文件中抽取相关表的定义,最后组装建表语句-非常好

    借助python工具从word文件中抽取表的定义,最后组装建表语句-非常好 --如有转载请以超链接的方式注明原文章出处,谢谢大家.请尊重每一位乐于分享的原创者 1.python脚本 ## -*- co ...