C类构造函数初始化列表
- 格式:docx
- 大小:15.87 KB
- 文档页数:4
C++——类继承以及类初始化顺序对于类以及类继承, ⼏个主要的问题:1) 继承⽅式: public/protected/private继承.这是c++搞的, 实际上继承⽅式是⼀种允许⼦类控制的思想. ⼦类通过public继承, 可以把基类真实还原, ⽽private继承则完全把基类屏蔽掉.这种屏蔽是相对于对象层⽽⾔的, 就是说⼦类的对象完全看不到基类的⽅法, 如果继承⽅式是private的话, 即使⽅法在基类中为public的⽅法.但继承⽅式并不影响垂直⽅向的访问特性, 那就是⼦类的函数对基类的成员访问是不受继承⽅式的影响的.⽐较(java): java是简化的, 其实可认为是c++中的public继承. 实在没必要搞private/protected继承, 因为如果想控制,就直接在基类控制就好了.2) 对象初始化顺序: c++搞了个成员初始化列表, 并确明确区分初时化跟赋值的区别. c++对象的初始化顺序是:(a) 基类初始化(b) 对象成员初时化(c) 构造函数的赋值语句举例:假设 class C : public A, public B {D d;//}则初始化的顺序是A, B, D, C的构造函数.这⾥基类的初始化顺序是按照声明的顺序, 成员对象也是按照声明的顺序. 因此 c(int i, int j) : B(i), A(j) {} //这⾥成员初始化列表的顺序是不起作⽤的;析构函数的顺序则刚好是调过来, 构造/析构顺序可看作是⼀种栈的顺序;⽐较(java): java中初始化赋值是⼀回事. ⽽且对基类的构造函数调⽤必须显⽰声明, 按照你⾃⼰写的顺序.对成员对象, 也叫由你初始化.没有什么系统安排的顺序问题, 让你感觉很舒服;3) 多继承问题: c++⽀持多继承, 会导致"根"不唯⼀. ⽽java则没有该问题;此外c++没有统⼀的root object, java所有对象都存在Object类使得很多东西很⽅便. ⽐如公共的seriall, persistent等等.4) 继承中的重载: c++中, 派⽣类会继承所有基类的成员函数, 但构造函数, 析构函数除外.这意味着如果B 继承A, A(int i)是基类构造函数, 则⽆法B b(i)定义对象. 除⾮B也定义同样的构造函数.c++的理由是, 假如派⽣类定义了新成员, 则基类初始化函数⽆法初始化派⽣类的所有新增成员.⽐较(java): java中则不管, 就算有新增对象基类函数没有考虑到, ⼤不了就是null, 或者你⾃⼰有缺省值. 也是合理的.5) 继承中的同名覆盖和⼆义性: 同名覆盖的意思是说, 当派⽣类跟基类有完全⼀样的成员变量或者函数的时候, 派⽣类的会覆盖基类的.类似于同名的局部变量覆盖全局变量⼀样. 但被覆盖的基类成员还是可以访问的.如B继承A, A, B都有成员变量a,则B b, b.a为访问B的a, b.A::a 则为访问基类中的a. 这对于成员函数也成⽴.但需要注意的是, 同名函数必须要完全⼀样才能覆盖. int func(int j)跟int func(long j)其实是不⼀样的. 如果基类,派⽣类有这两个函数, 则不会同名覆盖.最重要的是, 两者也不构成重载函数. 因此假如A有函数int func(int j), B有函数int func(long j). 则B的对象b.func(int)调⽤为错误的. 因为B中的func跟它根本就不构成重载.同名覆盖导致的问题是⼆义性. 假如C->B=>A, 这⾥c继承B, B继承A. 假如A, B都有同样的成员fun, 则C的对象c.fun存在⼆义性. 它到底是指A 的还是B的fun呢?解决办法是⽤域限定符号c.A::fun来引⽤A的fun.另外⼀个导致⼆义性的是多重继承. 假设B1, B2都继承⾃B, D则继承B1, B2. 那么D有两个B⽽产⽣⼆义性.这种情况的解决办法是⽤虚基类. class B1 : virtual public B, class B2:virtual public B, D则为class D : public B1, public B2. 这样D中的成员只包含⼀份B的成员使得不会产⽣⼆义性.⽐较(java). java中是直接覆盖. 不给机会这么复杂, 还要保存基类同名的东西. 同名的就直接覆盖, 没有同名的就直接继承.虚基类的加⼊, 也影响到类的初始化顺序. 原则是每个派⽣类的成员化初始化列表都必须包含对虚基类的初始化.最终初始化的时候, 只有真正实例化对象的类的调⽤会起作⽤. 其它类的对虚基类的调⽤都是被忽略的. 这可以保证虚基类只会被初始化⼀次.c++没有显式接⼝的概念, 我觉得是c++语⾔的败点. 这也是导致c++要⽀持组件级的重⽤⾮常⿇烦. 虽然没有显式的接⼝, 但c++中的纯虚函数以及抽象类的⽀持, 事实上是等同于接⼝设施的. 当⼀个类中, 所有成员函数都是纯虚函数, 则该类其实就是接⼝.java c++接⼝类(所有成员函数都是纯虚函数)抽象类类(部分函数是虚函数)对象类对象类C++构造函数调⽤顺序1. 如果类⾥⾯有成员类,成员类的构造函数优先被调⽤;2. 创建派⽣类的对象,基类的构造函数优先被调⽤(也优先于派⽣类⾥的成员类);3. 基类构造函数如果有多个基类,则构造函数的调⽤顺序是某类在类派⽣表中出现的顺序⽽不是它们在成员初始化表中的顺序;4. 成员类对象构造函数如果有多个成员类对象,则构造函数的调⽤顺序是对象在类中被声明的顺序⽽不是它们出现在成员初始化表中的顺序;5. 派⽣类构造函数,作为⼀般规则派⽣类构造函数应该不能直接向⼀个基类数据成员赋值⽽是把值传递给适当的基类构造函数,否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。
c++ 初始化列表构造函数C++中的初始化列表是一种用于在对象的构造函数中初始化成员变量的方法。
它可以让我们更加方便地初始化类中的成员变量,提高代码效率,同时也能够提高程序的可读性和可维护性。
在C++中,当我们声明一个类时,它的构造函数可以使用初始化列表来初始化类的成员变量。
初始化列表是构造函数的一部分,它由冒号(:)和一系列逗号分隔的初始化器组成。
在初始化列表中,我们可以使用成员变量的名称和初始值来初始化类的成员变量。
以下是一个示例类的声明,其中包含了一个拥有两个成员变量的Person类:```c++class Person {private:std::string name; // 姓名int age; // 年龄public:Person(const std::string& _name, int _age) : name(_name), age(_age) {}};```在上面的代码中,我们定义了一个名为Person的类,它有两个成员变量:姓名(name)和年龄(age)。
构造函数的初始化列表中给出了每个成员变量的初始值。
在这个例子中,我们使用了std::string类型的成员变量'name'和int类型的成员变量'age'。
在初始化列表中,我们使用了成员变量的名称和初始值来初始化它们。
这样可以让代码更加简洁、可读且易于维护。
在实际的开发中,我们经常需要初始化类的成员变量。
这时,使用初始化列表可以大大提高代码的效率和可读性。
除了在构造函数中初始化成员变量,我们还可以在类的定义中使用默认参数来初始化成员变量。
例如:```c++class Person {private:std::string name = 'Unknown'; // 姓名int age = 0; // 年龄public:Person() {}Person(const std::string& _name, int _age) : name(_name), age(_age) {}};```在上面的代码中,我们为Person类的成员变量'name'和'age'提供了默认值。
c++中冒号(:)的⽤法(1)表⽰机构内位域的定义(即该变量占⼏个bit空间)typedef struct _XXX{unsigned char a:4;unsigned char c;} ; XXX(2)构造函数后⾯的冒号起分割作⽤,是类给成员变量赋值的⽅法,初始化列表,更适⽤于成员变量的常量const型。
struct _XXX{_XXX() : y(0xc0) {}};(3) public:和private:后⾯的冒号,表⽰后⾯定义的所有成员都是公有或私有的,直到下⼀个"public:”或"private:”出现为⽌。
"private:"为默认处理。
(4)类名冒号后⾯的是⽤来定义类的继承。
class 派⽣类名 : 继承⽅式基类名{派⽣类的成员};继承⽅式:public、private和protected,默认处理是public。
2、类构造函数(Constructor)的初始化列表先说下什么叫构造函数吧(是不是啰嗦了?C++的⼈应该都知道了吧,还是以防万⼀⼀下)。
所谓构造函数,就是与类同名的函数,它与普通函数的区别在于,它没有返回类型。
在构造函数后⾯紧跟着冒号加初始化列表,各初始化变量之间以逗号(,)隔开。
下⾯举个例⼦。
class myClass{public :myClass();// 构造函数,⽆返回类型,可以有参数列表,这⾥省去~myClass();// 析构函数int a;const int b;}myClass::myClass():a(1),b(1)// 初始化列表{}上⾯的例⼦展⽰了冒号的这个⽤法,下⾯对这个⽤法进⾏⼏点说明:1)初始化列表的作⽤相当于在构造函数内进⾏相应成员变量的赋值,但两者是有差别的。
在初始化列表中是对变量进⾏初始化,⽽在构造函数内是进⾏赋值操作。
两都的差别在对于像const类型数据的操作上表现得尤为明显。
我们知道,const类型的变量必须在定义时进⾏初始化,⽽不能对const型的变量进⾏赋值,因此const类型的成员变量只能(⽽且必须)在初始化列表中进⾏初始化,即下⾯的代码将会出错:myClass::myClass(){a = 1;// 没错,效果相当于在初始化列表中进⾏初始化b = 1;// 出错,const变量不能进⾏赋值操作;}2)初始化的顺序与成员变量声名的顺序相同。
C++构造函数初始化列表与构造函数中的赋值的区别C++类中成员变量的初始化有两种⽅式:构造函数初始化列表和构造函数体内赋值。
⼀、内部数据类型(char,int……指针等) class Animal { public: Animal(int weight,int height): //A初始化列表 m_weight(weight), m_height(height) { } Animal(int weight,int height) //B函数体内初始化 { m_weight = weight; m_height = height; } private: int m_weight; int m_height; } 对于这些内部类型来说,基本上是没有区别的,效率上也不存在多⼤差异。
当然A和B⽅式不能共存的。
⼆、⽆默认构造函数的继承关系中 class Animal { public: Animal(int weight,int height): //没有提供⽆参的构造函数 m_weight(weight), m_height(height) { } private: int m_weight; int m_height; }; class Dog: public Animal { public: Dog(int weight,int height,int type) //error 构造函数⽗类Animal⽆合适构造函数 { } private: int m_type; } 上⾯的⼦类和⽗类编译会出错: 因为⼦类Dog初始化之前要进⾏⽗类Animal的初始化,但是根据Dog的构造函数,没有给⽗类传递参数,使⽤了⽗类Animal的⽆参数构造函数。
⽽⽗类Animal提供了有参数的构造函数, 这样编译器就不会给⽗类Animal提供⼀个默认的⽆参数的构造函数了,所以编译时报错,说找不到合适的默认构造函数可⽤。
c语言结构体初始化0
在C语言中,可以使用结构体来定义一个包含多个变量的大型数据结构。
初始化结构体时,可以使用以下语法将所有成员变量初始化为0:cstruct MyStruct {
int a;
char b;
float c;
};
struct MyStruct my_struct = {0};
在这个例子中,我们定义了一个名为MyStruct的结构体,它包含三个成员变量:a、b和c。
然后,我们创建了一个名为my_struct的结构体变量,并使用花括号中的初始化列表将其所有成员变量初始化为0。
如果只想将某些成员变量初始化为0,可以在初始化列表中使用逗号分隔各个成员变量,并为其指定初始值。
例如:
cstruct MyStruct {
int a;
char b;
float c;
};
struct MyStruct my_struct = {0, 0, 0.0}; // 将a、b、c初始化为0
在这个例子中,我们只将a、b和c三个成员变量初始化为0,而其他成员变量将自动被初始化为0。
c++派生类的构造函数C++是一门面向对象的编程语言,它允许程序员使用类和对象来封装数据和行为。
在C++中,派生类是基于已存在的类(称为基类)创建的一种新类。
派生类从基类继承了数据和方法,并且还可以添加新的属性和方法。
在C++中,派生类的构造函数是指创建派生类对象时所调用的函数。
派生类的构造函数负责初始化派生类对象中从基类继承的成员和派生类自己添加的成员。
本文将详细介绍C++派生类的构造函数。
在C++中,派生类的构造函数必须调用基类的构造函数,以初始化从基类继承的成员变量。
在创建派生类对象时,首先创建基类对象,然后再对派生类对象进行初始化。
1. 构造函数必须有与类名相同的名称。
2. 构造函数可以有参数,也可以没有参数。
3. 派生类必须调用基类的构造函数,以初始化从基类继承的成员变量。
下面是一个基类Person和一个派生类Student的定义:```cppclass Person{protected:string name;int age;public:Person(){}void setName(string n){name = n;}void setAge(int a){age = a;}};在定义派生类Student的时候,通过public继承了基类Person。
此时,派生类的构造函数必须调用基类的构造函数,以初始化从基类继承的成员变量name和age。
派生类新增加了一个成员变量grade,需要在自己的构造函数中进行初始化。
派生类构造函数可以有多种调用方式,具体如下:1. 用基类构造函数初始化列表初始化派生类对象初始化列表是C++语言提供的一种简洁的初始化成员变量的语法。
它使用冒号(:)后接一个以逗号分隔的初始化列表,在其中对派生类和基类成员变量进行初始化。
下面是Student类中使用初始化列表对基类成员变量进行初始化的方法:在上面的代码中,派生类Student的构造函数使用冒号后接一个初始化列表来初始化基类成员变量name和age。
构造函数初始化数组构造函数初始化数组是一种常见的数组初始化方式,它可以在定义数组时直接指定数组元素的初始值,从而方便地创建一个具有特定初始状态的数组。
在本文中,我们将介绍构造函数初始化数组的基本语法和用法,并探讨它在实际编程中的应用。
一、构造函数初始化数组的基本语法构造函数初始化数组的基本语法如下:```type arrayName[] = {value1, value2, ..., valueN};```其中,type 表示数组元素的数据类型,arrayName 表示数组的名称,value1, value2, ..., valueN 表示数组元素的初始值。
注意,数组元素的初始值必须与数组元素的数据类型相匹配,否则会导致编译错误。
例如,下面的代码定义了一个整型数组,它包含了三个元素,分别为 1、2 和 3:```int myArray[] = {1, 2, 3};```二、构造函数初始化数组的用法构造函数初始化数组的用法非常灵活,可以用于各种场合。
下面我们将介绍几个常见的用法。
1. 初始化静态数组构造函数初始化数组最常见的用法是初始化静态数组。
静态数组是指在程序运行期间不会改变大小的数组,它通常用于存储一组固定数量的数据。
例如,下面的代码定义了一个静态数组,它包含了五个整数,分别为 1、2、3、4 和 5:```int myArray[5] = {1, 2, 3, 4, 5};```这样,我们就可以通过下标访问数组元素,例如:```cout << myArray[0] << endl; // 输出 1cout << myArray[1] << endl; // 输出 2cout << myArray[2] << endl; // 输出 3cout << myArray[3] << endl; // 输出 4cout << myArray[4] << endl; // 输出 5```2. 初始化动态数组构造函数初始化数组也可以用于初始化动态数组。
《C++PrimerPlus》中的成员初始化列表对于构造函数Queue::Queue(int qs):Queue::Queue(int qs){front = NULL;rear = NULL;items = 0;qsize = qs;}当qsize为常量(即const int qsize)时,上述实现⽆法正常运⾏。
因为调⽤构造函数时,对象将在⼤括号中的代码执⾏之前被创建,亦即构造函数将先为4个成员变量分配内存,然后,程序进⼊到⼤括号中使⽤常规的赋值⽅法将值存储到内存中。
对于const数据成员,必须在执⾏到构造函数体之前,也就是创建对象时进⾏初始化。
通过成员初始化列表,可以实现上述⼯作。
成员初始化列表由逗号分割的初始化列表组成(前⾯带冒号),位于构造函数的参数列表之后,函数体括号之前。
如果数据成员名称为mdata,且需要初始化为val,则初始化器为mdata(val),在此,初始化与赋值的区别再次体现出来。
上述Queue类的构造函数以成员初始化列表语法表⽰如下:Queue::Queue(int qs) : qsize(qs), front(NULL), rear(NULL), items(0){}对于const类成员以及被声明为引⽤的类成员,必须使⽤这种语法。
这是因为引⽤与const数据类似,只能在被创建时进⾏初始化。
对于简单数据成员,使⽤成员初始化列表和在函数体中使⽤赋值没有什么区别,⽽对于本⾝就是类对象的成员来说,使⽤成员初始化列表的效率更⾼,因为在构造函数体中赋值需要⾸先调⽤类对象成员的默认构造函数,然后在调⽤赋值运算符对其进⾏赋值,⽽成员初始化列表语法可以减少⼀个步骤,直接使⽤类对象成员的复制构造函数进⾏初始化。
数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的排列顺序⽆关。
需要注意的是,不能将成员初始化列表语法⽤于构造函数之外的其他类⽅法。
派⽣类不能直接访问基类的私有成员,⽽必须通过基类⽅法进⾏访问,因此,派⽣类构造函数必须使⽤基类构造函数。
显式调用构造函数初始化1.引言1.1 概述在编程中,构造函数是用来创建和初始化对象的特殊成员函数。
在大多数情况下,构造函数会自动调用,以便在创建对象时执行必要的初始化操作。
然而,有时候我们可能想要显式地调用构造函数来进行特定的初始化。
显式调用构造函数的目的是为了更加灵活地控制对象的初始化过程,以满足特定的需求。
通过显式调用构造函数,我们可以传入特定的参数来对对象进行初始化,而不是仅仅依靠默认的构造函数进行初始化。
在C++中,我们可以使用构造函数的初始化列表来显式初始化对象的成员变量。
初始化列表是构造函数的一部分,在构造函数体之前使用冒号进行标识,并列出每个成员变量的初始化方式。
通过初始化列表,我们可以在构造函数中显式地设置每个成员变量的初值。
显式调用构造函数和初始化列表的使用可以提高代码的可读性和性能。
通过显式调用构造函数,我们可以清楚地指定初始化的顺序和方式,并且可以在对象创建的同时完成必要的初始化操作。
使用初始化列表可以减少不必要的临时对象的创建和销毁,提高代码的效率。
总之,显式调用构造函数和初始化列表是在C++中实现对象初始化的重要技术。
通过它们,我们可以更加灵活地控制对象的初始化过程,并且提高代码的可读性和性能。
在接下来的文章中,我们将深入探讨显式调用构造函数和初始化列表的具体用法和注意事项。
1.2文章结构1.2 文章结构本文主要探讨显式调用构造函数初始化的相关内容。
在引言部分,我们将概述本文的主要内容和目的。
接下来的正文部分将详细介绍显式调用构造函数的概念以及其在初始化过程中的应用。
具体地,我们将探讨显式调用构造函数的语法和用法,并举例说明其在不同情况下的实际应用场景。
在第二节中,我们将重点介绍初始化的概念及其与显式调用构造函数的关系。
我们将讨论不同的初始化方式,包括默认初始化、直接初始化以及拷贝初始化,并分析它们与显式调用构造函数的异同点。
我们还将探索显式调用构造函数在初始化过程中的优势和适用性,并与其他初始化方法进行比较。
类构造函数初始化列表
初始化列表地定义在使用编程地过程当中,常常需要对类成员进行初始化,通常地方法有两种:一种是构造函数内对类地成员赋值,一种则是使用初始化列表地构造函数显式地初始化类地成员.
构造函数初始化列表以一个冒号开始,接着是以逗号分隔地数据成员列表,每个数据成员后面跟一个放在括号中地初始化式.例如:{
: ;
;
构造函数初始化列表(): ()() {}
构造函数内部赋值()
{
;
;
}
}; 从技术上说,用初始化列表来初始化类成员比较好,但是在大多数情况下,两者实际上没有什么区别.第二种语法被称为成员初始化列表,之所以要使用这种语法有两个原因:一个原因是必须这么做,另一个原因是出于效率考虑初始化列表地必要性初始化和赋值对内置类型地成员没有什么大地区别,像上面地任一个构造函数都可以.但在一些情况下,初始化列表可以做到构造函数做不到地事情:
、类里面有类型地成员,它是不能被赋值地,所以需要在初始化列表里面初始化它;
、引用类型地成员(也就是名字成员,它作为一个现有名字地别名),也是需要在初始化列表里面初始化地,目地是为了生成了一个其名字成员在类外可以被修改而在内部是只读地对象;
、需要调用基类地构造函数,且此基类构造函数是有参数地;
、类里面有其他类类型地成员,且这个“其他类”地构造函数是有参数地.
举个例子:设想你有一个类成员,它本身是一个类或者结构,而且只有一个带一个参数地构造函数. { : ( ) { ... } }; 因为有一个显式声明地构造函数,编译器不产生一个缺省构造函数(不带参数),所以没有一个整数就无法创建地一个实例. * ; 出错!! * (); 如果是另一个类地成员,你怎样初始化它呢?答案是你必须使用成员初始化列表. { ; : (); };
必须使用初始化列表来初始化成员
() : () {……} 没有其它办法将参数传递给.
情况和其实一样地道理.如果成员是一个常量对象或者引用也是一样.根据地规则,常量对象和引用不能被赋值,它们只能被初始化. 初始化列表与构造函数赋值地效率比较首先把数据成员按类型分类并分情况说明:
.内置数据类型,复合类型(指针,引用)
在成员初始化列表和构造函数体内进行,两者在性能和结果上都是一样地
.用户定义类型(类类型)
两者在结果上相同,但是性能上存在很大地差别.
因为编译器总是确保所有成员对象在构造函数体执行之前初始化,所以对于用户自定义类型(类),在初始化列表中只会调用类地构造函数,在构造函数体中赋值就会先调用一次类地构造函数,然后再调用一次类地赋值操作符函数.
显然后者在性能上有所损失,特别对于构造函数和赋值操作符都需要分配内存空间地情况,使用初始化列表,就可以避免不必要地多次内存分配.
举个例子:假定你有一个类具有一个类型地成员,你想把它初始化为" .".你有两种选择:
、使用构造函数赋值()
{
使用赋值操作符(); (" .");
} 、使用初始化列表() : ((" .")) {} 编译器总是确保所有成员对象在构造函数体执行之前被初始
化,因此在第一个例子中编译地代码将调用来初始化,这在控制到达赋值语句前完成.在第二个
例子中编译器产生一个对:: ()地调用并将" ."传递给这个函数.结果是在第一个例子中调用了两
个函数(构造函数和赋值操作符),而在第二个例子中只调用了一个函数.
在地例子里这是无所谓地,因为缺省构造函数是内联地,只是在需要时为字符串分配内存(即,
当你实际赋值时).但是,一般而言,重复地函数调用是浪费资源地,尤其是当构造函数和赋值
操作符分配内存地时候.在一些大地类里面,你可能拥有一个构造函数和一个赋值操作符都要调
用同一个负责分配大量内存空间地函数.在这种情况下,你必须使用初始化列表,以避免不必要
地分配两次内存.
在内建类型如或者或者其它没有构造函数地类型下,在初始化列表和在构造函数体内赋值这两种
方法没有性能上地差别.不管用那一种方法,都只会有一次赋值发生.有些程序员说你应该总是用
初始化列表以保持良好习惯,但我从没有发现根据需要在这两种方法之间转换有什么困难.在编
程风格上,我倾向于在主体中使用赋值,因为有更多地空间用来格式化和添加注释,你可以写出
这样地语句:;
或者(, , ()); 初始化列表地成员初始化顺序初始化类成员时,是按照成员声明地顺序初始化地,而不是按照出现在初始化列表中地顺序.
因为一个类可以有多个构造函数,那么初始化列表可能各有不同,但是却只有一个析构函数,析
构函数地析构顺序是和构造地顺序相反地.如果按照初始化列表来初始化,而且有多个构造函数
地情况下,那么析构地时候就不能确定析构地顺序.只有按照声明地顺序,无论构造函数中初始
化列表是何顺序,都可以按照确定地顺序析构.
保持一致性最主要地作用是避免以下类似情况地发生:
{ ( , ); ; ;
}; ( , ) : (), (){} 你可能以为上面地代码将会首先做,然后做,最后它们有相同地值.但是编译器先初始化,然后是,,因为它们是按这样地顺序声明地.结果是将有一个不可预测地值.
有两种方法避免它,一个是总是按照你希望它们被初始化地顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明地顺序罗列这些成员.这将有助于消除混淆.b5E2R。