C++_派生类的构造函数及派生类和基类之间的特殊关系

派生类和基类的概念及派生类构造函数的原理:

创建一个叫做TableTennisPlayer的基类,记录会员的名字和是否有球桌。

 1 //声明一个基类
 2 class TableTennisPlayer
 3 {
 4 private:
 5     string firstname;
 6     string lastname;
 7     bool hasTable;
 8
 9 public:
10     TableTennisPlayer();
11     void Name() const;
12     bool HasTable() const {return hasTable;};
13     void ResetTable(bool v) {hasTable =v;};
14 }
15
16 //构造函数的定义
17 TableTennisPlayer::TableTennisPlayer(const string & fn,const string & ln ,bool ht):firstname(fn),lastname(ln),hasTable(ht) {}
18
19 //Name函数的定义
20 void TableTennisPlayer::Name() cosnt
21 {
22     std::cout << lastname << ", "<<firstname;
23 }

接下来声明一个派生类:

 1 //声明一个派生类,多了个排名的数据成员
 2 class RatedPlayer: public TableTennisPlayer
 3 {
 4 private:
 5     unsigned int rating;
 6 public:
 7     RatedPlayer(unsigned int r =0,const string & fn = "none",const string & ln ="none", bool ht = false);
 8     RatedPlayer(unsigned r, const TableTennisPlayer & tp);
 9     unsigned int Rating() const {return rating;}
10     void ResetRating (unsigned int r) {rating = r;}
11 };
12
13 //构造函数的实现
14 RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht)
15 {
16     rating =r;
17 }
18
19 RatedPlayer(unsigned r, const TableTennisPlayer & tp):TableTennisPlayer(tp),rating(r)
20 {
21 }

派生类不能访问基类的私有成员,而必须通过基类方法进行访问。

因此派生类构造函数必须使用基类构造函数;

创建派生类对象时,程序首先创建基类对象。从概念上讲,这意味着基类对象应当在程序进入派生类构造函数之前被创建。->C++使用成员初始化列表来完成这种工作。

RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht)

{

rating = r;

}

如果省略了基类构造函数的话:

RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht)

{

rating = r;

}

首先还是创建基类对象,如果不调用基类构造函数,程序将使用默认的基类构造函数

因此上述代码与下面等效:

RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer()

{

rating = r;

}

除非要使用默认构造函数,否则应该显式调用正确的基类构造函数。

还有一种构造函数代码:

RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):TableTennisPlayer(tp)

{

rating = r;

}

这里也是将TableTennisPlayer的信息传递给了TableTennisPlayer构造函数。

这种方式将调用基类的复制构造函数,如果基类没有定义复制构造函数,但又要使用它,则编译器将自动生成一个。

甚至还可以对派生类成员使用成员初始化列表语法:

RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):TableTennisPlayer(tp),rating(r)

{

}

派生类构造函数的要点总结:

首先创建基类对象;

派生类构造函数应通过成员初始化列表(TableTennisPlayer(tp))将基类信息传递给基类构造函数;

派生类构造函数应初始化派生类新增的数据成员(rating = r)。

如果没有提供显示构造函数,将使用隐式构造函数。

理解派生类对象创建过程的对象创建顺序:先创建基类对象,在创建派生类对象;

释放对象的顺序创建对象的顺序相反:首先执行派生类的析构函数,然后自动调用基类的析构函数。

成员初始化列表:(TableTennisPlayer(tp))

派生类构造函数可以使用初始化器列表机制将值传递给基类构造函数。

derived :: derived(type1 x, type2 y) : base(x,y)

{

...

}

Derived是派生类,base是基类。X和Y是基类构造函数使用的变量。

如果派生类构造函数接收到参数10和12,这种机制将把10和12传递给定义为接受这些类型的参数的基类构造函数。类只能将值传递回相邻的基类。虚基类除外,虚基类可以使用相同的机制将信息传递给相邻的基类,以此类推。如果没有在成员初始化列表中提供基类构造函数,程序将使用默认的积累构造函数。成员初始化列表只能用于构造函数。

派生类和基类的关系:(引用、指针)

  派生类可以使用基类的方法,条件是方法不是私有的。

基类指针可以再不进行显式类型转换的情况下指向派生类对象。

基类引用可以再不进行显式类型转换的情况下引用派生类对象。

但是基类指针和引用只能调用基类的方法。

C++中要求引用和指针类型与赋予的类型匹配,但这一规则对继承来说是例外。这例外是单向的,也就是说不能将基类对象和地址赋给派生类引用和指针。

但是这种关系是单向的,不能将基类对象和地址赋给派生类引用和指针。

这样要求是有道理的:如果允许基类引用隐式地调用派生类方法,则可以使用基类引用为派生类对象调用基类的方法。因为派生类继承了基类的方法,所以这样做不会有问题。

如果可以将基类对象赋给派生类引用,将发生什么情况?派生类引用能够为基类对象调用派生类方法,这是没有意义的。例如TableTennisPlayer没有rating成员。

用一个图总结如下:

基类引用和指针可以指向派生类对象,将出现一些很有意思的现象:

第一个例子

Show函数如下:

 1 void Show(const TableTennisPlayer & rt)
 2 {
 3     using std::cout;
 4     cout << "Name:";
 5     rt.name();
 6     cout << "\nTable:";
 7     if(rt.HasTable())
 8         cout<<"yes\n";
 9     else
10         cout <<"no\n";
11 }   

参数rt是一个基类引用,它可以指向基类对象或派生类对象,所以可以在Show()中使用TableTennisPlayer参数或Ratedplayer参数。

1 TableTennisPlayer player1("Tara","Boomdea",false);
2 RatedPlayer rplayer1(1110,"Mallory","Duck",true);
3 Show(player1);
4 Show(rplayer1);

对于形参为指向基类的指针的函数,也存在相似的关系。

总结来说:形参是指向基类引用的函数,可以传基类实参,也可以传派生类实参。

第二个例子(派生类对象可以对基类对象进行初始化)

RatedPlayer olaf1(1840,"Olaf","Loaf",true);

TabelTennisPlayer olaf2(olaf1);

第二行如何初始化?类定义中隐式复制构造函数:

TableTennisPlayer(const TableTennisPlayer &);

这个构造函数的形参是基类引用。因此它可以指向派生类对象。也就是说要将olaf2初始化为olaf1时,将要调用该构造函数,它复制了olaf1的firstname、lastname和hasTable成员。换句话说,它将olaf2初始化为嵌套在RatedPlayer对象olaf1中的TableTennisPlayer对象。

还可以将派生对象赋值给基类对象

RatedPlayer olaf1(1840,"Olaf","Loaf",true);

TableTennisPlayer winner;

winner = olaf1;

这种情况下程序将使用隐式重载赋值运算符;

TableTennisPlayer & operator=(cosnt TableTennisPlayer &) const;

可以看出基类引用指向的也是派生类对象,因此可以将olaf1的基类部分复制给winner。

原文地址:https://www.cnblogs.com/grooovvve/p/10126170.html

时间: 2024-10-26 04:51:05

C++_派生类的构造函数及派生类和基类之间的特殊关系的相关文章

C++:派生类的默认构造函数和拷贝构造函数调用基类构造函数的机制(含程序验证)

1.如果基类定义了不带参数的默认构造函数,则编译器为派生类自动生成的默认构造函数会调用基类的默认构造函数. 2.如果基类定义了拷贝构造函数,则编译器为派生类自动生成的拷贝构造函数同样会调用基类的拷贝构造函数. 3.如果基类定义了带参数的构造函数,派生类没有定义任何带参数的构造函数,则不能直接调用基类的带参构造函数,程序编译不通过. 代码如下: #include<iostream> using namespace std; //基类Game,定义了两个构造函数和一个拷贝构造函数 class Ga

虚基类练习:动物(利用虚基类建立一个类的多重继承,包括动物(animal,属性有体长,体重和性别),陆生动物(ter_animal,属性增加了奔跑速度),水生动物(aqu_animal,属性增加了游泳速度)和两栖动物(amp_animal)。)

Description 长期的物种进化使两栖动物既能活跃在陆地上,又能游动于水中.利用虚基类建立一个类的多重继承,包括动物(animal,属性有体长,体重和性别),陆生动物(ter_animal,属性增加了奔跑速度),水生动物(aqu_animal,属性增加了游泳速度)和两栖动物(amp_animal).其中两栖动物保留了陆生动物和水生动物的属性. Input 两栖动物的体长,体重,性别,游泳速度,奔跑速度(running_speed) Output 初始化的两栖动物的体长,体重,性别,游泳速度

派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good

大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸.我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree是最底层基类(非虚类), CSamplerTree(虚类)派生自CTree,CMSamplerTree,CASamplerTree派生自CSamplerTree,                                                         CTree中包括两个成员

私有继承:派生类指针不能隐示的转换为基类指针

class base{public: virtual void display(){ cout << "base" << endl; } };class drive : private base{public: virtual void display(){ cout << "drive" << endl; }};int _tmain(int argc, _TCHAR* argv[]){ //base* b = new

Android--将实体类转化成Json和Map的基类

package com.newair.talk.base; import android.text.TextUtils; import com.google.gson.Gson; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 公共请求 */ public class BaseRequest { /** * 将实体

C++:派生类的构造函数和析构函数

4.2 派生类的构造函数和析构函数4.2.1 派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数:当撤销派生类对象时,则先执行派生类的派生类的析构函数,随后再执行基类的析构函数. //例4.5 派生类的构造函数和析构函的执行顺序 #include<iostream> using namespace std; class Base{ //声明基类Base public: Base() { cout<<"Co

C#中派生类调用基类构造函数用法分析

这里的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.当基类中没有自己编写构造函数时,派生类默认的调用基类的默认构造函数例如: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyBaseClass { } public class MyDerivedClass : MyBaseClass {   public MyDerivedClass()   {    Console.WriteLine("我是子

关于C#中派生类调用基类构造函数的理解

(1)当基类中没有自己编写的构造函数时,派生类默认条用基类的构造函数 (2)当基类中有自己编写的构造函数时,要在基类中添加无参的构造函数 Java代码   public class MyBaseClass { public MyBaseClass() { } public MyBaseClass(int i) { Console.WriteLine("我是基类带一个参数的构造函数"); } } public class MyDerivedClass : MyBaseClass { pu

转 关于C#中派生类调用基类构造函数的理解

关于C#中派生类调用基类构造函数的理解 .c#class       本文中的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.  当基类中没有自己编写构造函数时,派生类默认的调用基类的默认构造函数 Ex: public class MyBaseClass    {    } public class MyDerivedClass : MyBaseClass    {        public MyDerivedClass()        {            Con