3.6 C++继承机制下的构造函数
参考: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++继承机制下的构造函数的更多相关文章
- c++ 16 this 和 继承 及继承机制中的构造函数 与 析构函数
#include <iostream> #include <string> using namespace std; class Animal { public: Animal ...
- 3.8 C++继承机制下的析构函数
参考:http://www.weixueyuan.net/view/6365.html 总结: 构造函数的执行顺序是按照继承顺序自顶向下的,从基类到派生类,而析构函数的执行顺序是按照继承顺序自下向上, ...
- Javascript 构造函数原型继承机制
我们先聊聊Js的历史,1994年Netscape公司发布了Navigator浏览器0.9班.这是历史上第一个比较成熟的网络浏览器.轰动一时.但是,这个版本的浏览器只能用来浏览,不具备交互功能,最主要的 ...
- js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法。
js最好的继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法. function ClassA(sColor) { this.color = sColor; } Class ...
- js一种继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法。
js一种继承机制:用对象冒充继承构造函数的属性,用原型prototype继承对象的方法. function ClassA(sColor) { this.color = sColor; } ClassA ...
- Javascript继承机制总结 [转]
转自:http://bbs.csdn.net/topics/260051906 Javascript继承 一直想对Javascript再次做一些总结,正好最近自己写了一个小型Js UI库,总结了一下J ...
- javascript继承机制 & call apply使用说明
一.继承机制 1.对象冒充:构造函数使用 this 关键字给所有属性和方法赋值,可使 ClassA 构造函数成为 ClassB 的方法,然后调用它. function ClassZ() { this. ...
- JavaScript中继承机制的模仿实现
首先,我们用一个经典例子来简单阐述一下ECMAScript中的继承机制. 在几何学上,实质上几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形. ...
- 深入浅出理解Javascript原型概念以及继承机制(转)
在Javascript语言中,原型是一个经常被讨论到但是有非常让初学者不解的概念.那么,到底该怎么去给原型定义呢?不急,在了解是什么之前,我们不妨先来看下为什么. Javascript最开始是网景公司 ...
随机推荐
- Python自学:第二章 删除空白
lstrip:剔除开头空白 strip:剔除两段空白 rstrip:剔除末尾空白 favorite: 最喜欢的 >>>favorite_language = "Python ...
- android-------Java 常问的基础面试题
1."=="和equals方法究竟有什么区别? ==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个 ...
- Jamie and Tree CodeForces - 916E (换根)
大意: n节点树, 每个点有权值, 三种操作: 1,换根. 2, lca(u,v)的子树权值全部增加x. 3, 查询子树权值和. 先不考虑换根, 考虑子树x加v的贡献 (1)对fa[x]到根的树链贡献 ...
- memtrack: Couldn't load memtrack module (No such file or directory) 的问题解决
通过了编译,可是在模拟器运行时,却出现stopping…….查看logcat,发现出现错误: E/memtrack: Couldn't load memtrack module (No such fi ...
- 373. Find K Pairs with Smallest Sums (java,优先队列)
题目: You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Def ...
- PAT 1035 Password
1035 Password (20 分) To prepare for PAT, the judge sometimes has to generate random passwords for ...
- MongoDB 教程(一):了解 NoSQL
概述: MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数据库之间的产品, ...
- Xshell中文乱码怎么处理?
改成如下图:
- C++ leetcode::Reverse Integer
第一天上课,数据库老师说对于计算机系的学生,凡是在课本上学到的专业知识都是过时的.深以为然,感觉大学两年半真的不知道学了什么,为未来感到担忧,C++也不敢说是精通,入门还差不多.最近丧的不行,不管怎么 ...
- maven plugins
<build> <finalName>lessons</finalName> <plugins> <plugin> <groupId& ...