-
虚基类声明,初始化及调用顺序
2012-07-29 00:19:501.如果虚基类中定义有带参数的构造函数,并且没有默认定义构造函数,则整个继承结构中,所有直接或间接的派生类必须在构造函数的成员初始化列表中列出虚基类构造函数的调用。 2.建立对象时,如果这个对象中含有从...1.如果虚基类中定义有带参数的构造函数,并且没有默认定义构造函数,则整个继承结构中,所有直接或间接的派生类必须在构造函数的成员初始化列表中列出虚基类构造函数的调用。
2.建立对象时,如果这个对象中含有从基类继承来的成员,则虚基类的成员有最远派生类的构造函数通过调用虚基类的构造函数进行初始化的,该派生类的其他基类对虚基类构造函数的调用都自动忽略。
3.如果同一层次中同时有虚基类和非虚基类,则先调用虚基类构造函数,在调用非虚基类构造函数,最后调用派生类构造函数。
4.当有多个虚基类,或多个非虚基类时,调用:先左后右,自上而下。
5.如虚基类由非虚基类派生来,则先调用基类构造函数,在调用派生类构造函数。
class 派生类:virtual 继承方式 类名{- - - - -}
例子如下:
#include<iostream> using namespace std; class Base{ public: Base(int sa) { a=sa; cout<<"constructing Base"<<endl; } protected: int a; }; class Base1:virtual public Base{ public: Base1(int sa,int sb):Base(sa) { b=sb; cout<<"constructing Base1"<<endl; } protected: int b; }; class Base2:virtual public Base{ public: Base2(int sa,int sc):Base(sa) { c=sc; cout<<"constructing Base2"<<endl; } protected: int c; }; class Deriverd:public Base1,public Base2{ public: Deriverd(int sa,int sb,int sc,int sd):Base(sa),Base1(sa,sb),Base2(sb,sc) { d=sd; cout<<"constructing Deriverd "<<endl; } void show() { cout<<"a="<<a<<endl; cout<<"b="<<b<<endl; cout<<"c="<<c<<endl; cout<<"d="<<d<<endl; } private: int d; }; int main() { Deriverd dd(10,20,30,40); dd.show(); return 0; }
-
虚基类的初始化
2008-03-13 13:55:00B的直接派生类,D1和D2,其初始化列表中要显式地调用B的构造函数;2. B的间接派生类,F,其初始化列表也要显示的调用B的构造函数。这是因为虚拟派生过程中,只有一个虚拟基类的子对象,不能因为多重派生,而对该子...假设有如下的类:
基类B,虚继承派生类D1, D2, 最终派生类F,多继承自D1, D2。1. B的直接派生类,D1和D2,其初始化列表中要显式地调用B的构造函数;2. B的间接派生类,F,其初始化列表也要显示的调用B的构造函数。这是因为虚拟派生过程中,只有一个虚拟基类的子对象,不能因为多重派生,而对该子对象进行多次,甚至相互矛盾的初始化。因此,只能在最终派生类F中显示的初始化虚拟基类。综合上述两个方面:只要在类的层次中存在虚继承,则每个类都要显式地初始化虚基类。另外,在F的初始化过程中,因为其直接基类D1,D2虚继承自B,所以,D1,D2对B的初始化也自动被抑制了。也就是说,在F中,也只有其对虚拟基类的显式初始化起作用。 -
虚继承和虚基类
2019-08-02 14:27:27在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。 对最终的派生类来说,虚基类是间接基类,而不是直接基类。 这跟普通继承不同,在普通继承中,派生类...在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。
对最终的派生类来说,虚基类是间接基类,而不是直接基类。
这跟普通继承不同,在普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
在最终派生类的构造函数调用列表中,不管各个构造函数出现的顺序如何,编译器总是先调用虚基类的构造函数,再按照出现的顺序调用其他的构造函数;而对于普通继承,就是按照构造函数出现的顺序依次调用的。
-
C++ Primer Plus 第14章 虚基类和类模板等
2020-02-12 12:00:14派生类对象的构造函数初始化,构造函数在成员初始化列表中使用基类类名来调用特定的基类构造函数 V2(int &w,double &q):V1(w){} //v1是基类,这种情况初始化列表中是用基类名称 而一个类中包含其他类对象...1.成员对象的列表初始化和继承列表初始化的区别
派生类对象的构造函数初始化,构造函数在成员初始化列表中使用基类类名来调用特定的基类构造函数
V2(int &w,double &q):V1(w){} //v1是基类,这种情况初始化列表中是用基类名称
而一个类中包含其他类对象作为成员的话,构造函数在成员初始化列表中使用成员名,比如
Student(string &s,valaray<double> &a):name(s),scores(a){} //name和scores是私有成员类对象
2.列表初始化顺序
当初始化列表包含多个项目时,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序。当一个成员的值作为另一个成员的初始化表达式的一部分时,初始化顺序就非常重要,所以编写一定要规范。
3.如果成员变量是一个对象,应该怎么调用该对象的类方法
一种方法是类对象调用自己的方法,然后在方法中使用包含的私有成员对象来调用该对象所属的类方法。
另外就是如果该对象是一个公共成员,那么可以通过(.)调用
比如:class Test { private: string s1; public: Test(const string& s) :s1(s) { cout << s1 << endl; } unsigned int len() { return s1.length(); } }; int main() { string s = "string"; Test t1("stringasasd"); cout<<t1.len(); //t1.s1.length; }
4.私有继承
私有继承不继承公共接口,这相当于将基类设置为派生类的私有对象成员,只不过这种对象没有名称,具体的代码实现大概如下所示:
class Student:private string,private valarray<double> { private: .... public: Student(const *str,const double *pd,int n):string(str),valarray<double>(pd,n);//构造函数 }
以上我们需要注意的有两点:
1.继承的时候,类名称后面跟的是private关键字,多重继承几个类就有几个private,中间用逗号隔开。但是不添加关键字private在私有继承来说也是可以的,因为编译器默认继承是私有派生,但是对于公有继承和保护继承,则必须使用public或protected来限定每一个基类。
2.派生类构造函数初始化的时候,因为继承的两个类并没有实际的名称,因此它使用类名初始化而不是成员名来标识构造函数。所以,对于构造函数,包含私有变量为类对象的情况相当于成员变量初始化,而对于私有继承,相当于调用基类构造函数。5.如果私有继承,应该怎么调用基类的类方法
与公有继承一样,可以在方法内用作用域解析运算符来访问基类方法
double Student::Average()const { if(valarray::size() > 0 ) return valarray::size::sum / valarray::size::size(); else return 0; }
6.访问基类对象
作用域解析符可以访问基类的方法,但是如果要使用基类对象本身,可以使用强制转换,即把派生类类型转换为基类类型,并且返回*this,就可以返回基类对象了。
7.访问基类的友元函数
友元函数不能继承,但是和公有继承一样,可以通过显式强制转换为基类类型来调用正确的函数,但是这里需要注意两点:
1.无论是公有继承还是私有继承,友元函数要访问基类对象,都需要显式地转换类型,不然会导致递归调用。
2.如果不显式转换类型,编译器不会自动将派生类引用或指针赋值给基类引用或指针。8.使用using关键字来重新定义访问权限
class Student:private string,private valarray<double> { private: .. public: using valarray<double>::min; using valarray<double>::max; }
这样就可以将私有继承而来的对象方法变为公有方法,需要注意的是using声明只使用成员名——没有圆括号、函数特征和返回类型。
9.什么是虚基类,为什么要用虚基类?
1.当一个派生类多重继承了两个基类,而这两个基类中刚好有相同的部分,即又都继承了一个相同的基类,那么这个时候就会出现二义性问题。比如当这个派生类要访问两个基类相同的部分,这个时候编译器检测到两个地址可供选择,当然我们可以通过显式转换来指定我们需要访问的对象,但是无疑这样做是麻烦的。
所以为了解决这种问题,c++引入了一种新技术——虚基类,虚基类指的是从多个类(它们的基类相同)派生出的对象只继承一个基类对象,该技术是使用关键是virtual来实现的,比如:class singer : virtual public Worker{...}; //virtual 和public 不分先后 class Waiter : virtual public Worker{...};
class SingerWaiter : public singer,public Waiter{...}; //SingerWaiter对象就只包含Worker对象的一个副本,而不是两个。
2.当我们并不需要基类的多个拷贝,而只是需要继承一个基类对象的的特征的时候,就需要虚基类,但是虚基类会要求额外的运算时间,而且有缺点。
10.虚基类的构造规则
一般的派生类C构造函数只能调用它的基类B构造函数,它的基类B构造函数又将调用B的基类A的构造函数,这是一种自动传递的层次设计,但是在虚基类中,这种自动传递将失效,C++在基类是虚时,禁止信息通过中间类自动传递给基类。但是我们知道派生类构造函数生效之前必须要构造基类的对象组件,所以在基类为虚的时候,编译器将使用基类的默认构造函数。
如果不希望默认构造函数来构造基类对象,则需要显式地调用所需的基类构造函数。比如:SingerWaiter(const Worker &wk,int p = 0,int v = Singer::other)::Worker(wk),Waiter(wk,p),Singer(wk,v){} //worker为Waiter和Singer的基类,SingerWaiter 是Waiter和Singer的派生类
对于虚基类,这种方式调用是可以的,但是对于非虚基类则是非法的。
11.虚基类中对祖先基类方法的调用
对于单继承,派生类如果没有重新定义一个方法,那么调用将会是最近祖先的方法定义,但是在多重继承中,每个直接祖先都有一个同名函数,这将导致调用二义性。
有两种方法可以解决这个问题:
1.用作用域解析符指定要使用哪个祖先基类的同名方法。
2.重新定义一个同名方法,其中包含基类方法,然后添加自己需要的代码,最后将这些组件组合起来。12.混合使用虚基类和非虚基类将会产生什么结果?
只需要记住使用虚基类只有一个祖先对象,而每一个非虚派生祖先都会继承一个基类祖先,比如B是X、Y的虚基类,而是A、C的非虚基类,那么当有一个M派生类继承了XYAC这四个基类,那么它会有3个B类子对象。
13.类模板的定义
1.模板类函数定义之前需要加上以下开头
template <class Type> 或者 template <typename Type>
2.类限定符需要加上,比如
Stack<Type>::
3.类模板的使用,比如新建一个模板类对象:Stack<double> nets;
需要添加类型,如果是Stack<int>
将使用int替换掉模板中所有的Type
4.最重要的一点是不能将模板成员函数放在独立的实现文件中,这是因为模板不是函数,它们不能单独编译。为此,最简单的方法就是将所有模板信息都放在头文件中。14.模板类中的表达式参数(非类型参数)的注意事项
template <class T,int n> //其中的int类型参数就叫做表达式参数也称为非类型参数
表达式参数有一些限制,表达式参数可以是整型、枚举、引用或指针,因此double n不合法,但是double *n,double &n合法。
模板代码不能修改参数的值,也不能使用参数的地址。15.模板类继承
模板类可以作为基类,也可用作派生类,比如:
template <typename T> class Array { private: T entry; ... public: ... } template <typename Type> class GrowArray : public Array<Type>{...} //继承 template <typename Tp> class Stack { Array<Tp> ar; //将Array<>用作组件类 ... }
16.默认类型模板参数
类模板的另一项新特性是,可以为类型参数提供默认值
template<class T1,class T2 = int> class Tim{...};
这样,可以省略T2的初始化,编译器将使用int
17.模板的具体化
1.隐式实例化
在.cpp文件中定义一个对象时,该对象指出所需的类型,而编译器使用通用的模板提供的方案生成具体的类定义,也就是说,编译器在需要对象之前,不会生成类的隐式实例化。
2.显示实例化
当使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显示实例化。声明必须位于模板定义所在的名称空间中,例如:template class ArrayTp<string,100>;
在这种情况下,虽然没有创建ArrayTp<class T1,class T2>对象,但是编译器也将生成类声明,虽然尚未请求这个类的对象
18.显式具体化
有时候,我们可能需要对某一种或者多种类型进行专门的类代码编写,使其与一般类型行为不同。在这种情况下,可以创建显式具体化。
template<>class ClassName<specialized-type-name>{...};
19.模板类和友元
1.模板类的非模板友元函数
该友元函数将是模板所有实例化的友元,不管typename是什么类型。
2.如果要为友元函数提供模板类参数,必须要声明明确的typename类型
比如:friend void report(HashFriend &); //这种是错误的 friend void report(HashFriend<T> &) //这种是正确的
为什么需要具体化呢,这是因为友元函数不是模板函数,它是一个实实在在的函数,编译器在运行它的时候是需要明确指定模板类的类型的,这意味着必须要为使用的友元定义显示具体化。
2.模板类的约束模板友元函数
这种情况分为三步
第一步:首先在类定义之前声明每个模板函数
第二步:然后再在类中将模板声明为友元
第三步:为友元提供模板定义
2.模板类的非约束模板友元函数
这种情况就是每个函数具体化都是每个类具体化的友元,比如:template<typename C,typename D>friend void show2(C& ,d &); //友元函数声明
-
多继承和虚基类
2015-07-10 15:51:00一.多继承机制存在哪些问题,怎么解决这些问题? ——歧义性:相同名称的...1.若在虚基类中定义了带参数的构造函数,而没有定义默认构造函数,则必须在所有的直接或间接派生类中使用成员初始化列表调用虚基类的构... -
继承和派生、虚基类——《C++ Primer Plus》
2019-12-01 15:26:26构造函数和析构函数都不能继承。 派生类的构造函数承担着对基类中数据成员初始化和对派生类自身数据成员初始化的双重任务。...派生类构造函数的成员初始化列表中应该显式地包含基类中带参数的构造函数,或者... -
C++多重继承与虚基类
2012-03-28 15:21:041. C++允许一个派生类从多个基类继承,这种...2. 多重继承的构造函数和析构函数:多重继承中初始化的次序是按继承的次序来调用构造函数的而不是按初始化列表的次序, 比如有class A:public B, public C{}那么在定义类 -
C++ (P160—)多继承 二义性 虚基类 “向上转型”
2019-09-28 09:58:211 多继承中,必须给每个基类...3 由于c++中不允许对类成员进行初始化,但是在编程时需要用特定的值去初始化派生类的对象,这时需要通过为派生类定义一个带有初始化列表的构造函数来实现。 class D:public B,pri... -
C++ 虚基类问题、继承体系中的构造函数执行过程。
2012-06-29 11:45:27本篇博客是做了网上的一个题目,然后总结而来的,题目并不难,关键是你能否真正理解其中的过程。 解题思路: ...初始化列表负责对基类部分和成员部分进行空间的分配和初始化,其实就是调用他们 -
派生类构造函数的初始化列表中包含 初始化基类数据成员、新增内嵌对象数据及新增_「C++ Primer plus 心得」...
2020-11-30 20:31:57本章内容包括has-a关系包含对象成员的类模板类valarray私有和保护继承多重继承虚基类创建模板类使用模板类模板的具体化14.1 包含对象成员的类14.1.1valarray 类简介valarray类是头文件valarray支持的,它支持诸如将... -
C++ Primer Plus P480~P533 公有派生、联编、虚函数、抽象基类、继承和动态内存分配
2020-09-16 19:58:08类继承 公有派生 ...创建派生类对象时,程序首先创建基类对象,C++中使用成员初始化列表语法完成这种工作。形如Apple::Apple(int a,float b):Fruit(a),这代表将参数中的a作为实参传递给基类构造函数,若 -
构造函数不能虚,多态基类析构函数应为虚
2016-10-02 18:07:43由于派生类和基类之间的关系是一种IS-A的关系,所以通常用基类指针或引用指向派生类对象。 为了构造对象,constructor必须要事先...初始化列表初始化变量 执行构造函数函数体部分 控制权返回至调用者 对于派生类(w -
c++ 构造函数初始化列表,构造函数的调用顺序
2016-08-05 17:00:28列表初始化要比赋值初始化效率高,养成好习惯使用初始化列表哟 !而且对于const修饰的变量和引用型变量必须用这种方式初始化 列表初始化的顺序: 初始化的顺序与列表中书写的顺序无关,与继承的顺序(不写父类初始... -
c++中基类的虚继承__2018.05.07
2018-05-07 22:50:401.c++构造函数的初始化列表只是指定初始化的方法,而并没有指定初始化(构造)的顺序。c++初始化的顺序是由继承基类的顺序决定的。构造的顺序由继承列表决定。2.继承会继承属性(成员变量)和行为(处理方法)。3.3.... -
C++ 类继承:构造函数与析构函数调用顺序,派生类和基类之间的特殊关系,公有继承及其他
2019-03-04 12:38:15文章目录一、派生类构造函数与基类构造函数二、创建与销毁派生类对象时,构造函数和析构函数的调用三、派生类和基类之间的特殊关系四、公有继承(一)、何为公有继承(二)、多态公有继承(三)、虚函数的工作原理... -
关于虚函数和纯虚函数,以下说法正确的是()?
2019-08-19 16:44:54虚基类的构造函数在非虚基类之前调用(最终继承类构造函数中,成员初始化列表同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行) 带有纯虚函数的类不能直接实例化 子类实现... -
c++11初始化
2018-09-17 22:30:14统一的初始化方法,c++11中所有数据类型都可以使用初始化列表,但是对于结构体 或者类,需要满足一定的条件: (1)无用户自定义构造函数。 (2)无私有或者受保护的非静态数据成员 (3)无基类 (4)无虚函数 (5)... -
在保护继承中基类的共有成员_「C++ Primer plus 心得」13.类继承
2021-01-09 17:47:24构造函数成员初始化列表; 向上和向下强制转换; 虚成员函数; 早期(静态)联编与晚期(动态)联编; 抽象基类; 纯虚函数; 何时及如何使用公有继承类库由类声明和实现构成的。通常,类库是以源代码的方式提供... -
基类的构造函数也可以被继承_「C++ Primer plus 心得」13.类继承
2020-11-24 07:05:45构造函数成员初始化列表; 向上和向下强制转换; 虚成员函数; 早期(静态)联编与晚期(动态)联编; 抽象基类; 纯虚函数; 何时及如何使用公有继承类库由类声明和实现构成的。通常,类库是以源代码的方式提供... -
继承(多态和虚析构函数)
2019-03-03 21:12:24派生类不能直接访问基类的私有成员,必须通过...派生类构造函数初始化基类私有成员,采用成员初始化列表。 Class_name::Class_name(int a,int b, int c):Base_class(a,b) { } 派生类构造函数要点: 首先创... -
C++之多继承与虚继承
2018-05-09 22:57:001. 多继承 1.1 多继承概念 一个类有多个直接基类的继承关系称为多继承 多继承声明语法 class 派生类名 : 访问控制 基类名1, 访问控制 基类名...多个基类的派生类构造函数可以用初始化列表调用基类构造函数来初始化... -
C++学习笔记----多重继承和虚继承
2021-02-27 14:00:33C++体系中允许多重继承和虚继承,但是所重继承和虚继承应该注意一些情况,比如函数的调用冲突、初始化过程等。 1.多重继承 多重继承就是指从多个直接基类中产生的派生类,多重继承产生的派生类中包含其所有基类的... -
c++的构造函数调用顺序
2019-12-17 19:37:411.若同一层次中同时包含虚基类和非虚基类类,先调用虚基类的构造函数,再调用非虚基类的构造函数。 2.在虚基类中定义有带参数的构造函数,并且没有定义默认形式的构造函数,则整个继承结构中,所有直接或间接的派生... -
第18章 用于大型程序的工具 18.3 多重继承和虚继承
2020-03-24 22:59:53多重继承实在是一个折磨人的...派生类构造函数初始化所有基类的时候,我们一般会在初始化列表中调用基类的构造函数。一个多重继承的派生类其构造顺序是按照派生列表中声明的顺序进行的。 对于多重继承,一个相同的基... -
C++要点(五)-多重继承和虚继承
2010-07-20 15:16:00<br />1.在多重继承中,构造函数的初始化式只能控制用于初始化基类的值,不能控制基类的构造次序。基类的构造次序按照... 则基类构造函数调用的次序是A, B, C 而不是初始化列表中的A, C, B 析构函数 -
继承、虚函数与多态类
2020-05-19 01:02:25基类地初始化——初始化列表 构造函数与析构函数 继承代码实例 虚函数 多态的实现 抽象类 多态的应用实例 课程感想 知识点总结+应用 继承 继承机制使用已经定义的类作为基础建立新的类定义,新的类是原有... -
【c++】多重继承与虚继承
2014-07-15 15:19:00派生类的构造函数初始化列表将实参分别传递给每个直接基类,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始化列表中基类的顺序无关。 类型转换与多个基类 编译器不会在派生类向... -
C++构造函数析构函数虚函数
2012-10-13 15:05:04在C++中,创建派生类对象时,基类和派生类的构造函数和析构函数的调用顺序正好相反: ...对基类成员和派生类子对象成员的初始化必须在成员初始化列表中进行,新增成员的初始化既可以在成员初始化列表中进行 -
C++入门学习:多继承的概念及其二义性、虚继承的概念
2018-03-19 22:47:55多继承 一个类有多个直接基类的继承关系称为多继承...如果基类没有无参构造的话,需要在对象初始化列表中显示调用基类的构造函数基类的构造顺序和在对象列表中调用顺序无关,和在继承时候的继承顺序有关基类指针可以... -
深度探索C++ 对象模型【第五章3】
2017-11-11 09:02:121:总结一下拷贝赋值运算符 较为复杂,不做细节...2:Explict Initialization List 要注意和成员初始化列表相区别开来。 Point A = {1,2,3};//显示初始化列表 //与构造函数中的成员初始化列表有明显的区别 3:总