C++初始化类的成员有两种方法:
①使用构造函数;
②使用初始化列表。
两种方法都可。但是有些情况下,只能用第一种,而且通常情况下用第一种也会效率高些。其实,第一种方法是真正的初始化(initialization),而在构造函数内实现的“=”操作其实是赋值(assign)。(构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,计算阶段即为赋值操作过程。)
以下几种方法需要使用第二种方法:
①对常量(const)进行初始化。
我们知道普通变量编译器都会默认的替你初始化。他们既能初始化,也能被赋值的,而常量(const)按照其意思只能被初始化,不能赋值。否则与变量就无区别了。所以常量成员(const member)只能用成员初始化列表来完成他们的“初始化”,而不能在构造函数内为他们“赋值”。
例如
class Shape
{
const int m_size; //const 常量
float m_width;
float m_height;
public:
Shape(int s,float w,float h):m_size(s) //只能在这初始化
{
//m_size =s; //在初始化将出错
m_width = w;
m_height = h;
}
};
②如果一个类的对象做另一个类的成员,那么为了初始化这个成员需要为其构造函数传递参数。
我们知道类的对象的初始化其实就是调用他的构造函数完成,如果没有写构造函数,编译器会为你默认生成一个。如果你自定义了带参数的构造函数,那么编译器将不生成默认构造函数。这样这个类的对象的初始化必须有参数。如果这样的类的对象来做另外某个类的成员,那么为了初始化这个成员,你必须为这个类的对象的构造函数传递一个参数。同样,如果你在包含它的这个类的构造函数里用“=”,其实是为这个对象“赋值”而非“初始化”它。所以一个类里的所有构造函数都是有参数的,那么这样的类如果做为别的类的成员变量,你必须显式的初始化它,你也是只能通过成员初始化列表来完成初始化。例如,
class B{
......
}
class A
{
public:
B member_b;
A();
}
A::A():B(...) //你必须显式初始化它,因为他的所有构造函数
//都是有参数的,之后才能被赋值。
{
B=...; //因为如上所写,已经初始化了,才能被赋值,否则错误。
}
——————————————————————————————————————
初始化顺序:
class test
{
const int a;
std:string str;
object o;
test():str(“df”),o(null),a(0)
{
}
};
黄色的既是初始化列表,他们会在构造函数正式调用前被调用,且他们的初始化顺序并不是根据 初始化列表中出现的顺序,而是他们声明的顺序来初始化。如上:
初始化顺序是:a, str, o;
③如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。
class A
{…
A(int x); // A 的构造函数
};class B : public A
{…
B(int x, int y);// B 的构造函数
};B::B(int x, int y): A(x) // 在初始化表里调用A 的构造函数
{
…
}
④对于引用类型的成员变量的初始化必须使用初始化列表。
C++中的引用是其他变量的别名。声明一个引用型变量,需要给他一个初始化值,与值类型不同的是,引用型本身并不能保存数据,只同指针一样指向其他的变量、常量。
比如
int a = 0;
int& b = a;
下边这个例子综合了以上几种情况:
#include <iostream.h>
#include <conio.h>
class CPoint // 基类
{
public:
int x,y;
CPoint(int ax=0,int ay =0)
{
x = ax;
y = ay;
cout<<"CPoint初始化赋值部分。x :"<<x<<"y:"<<y<<endl;
}
};
class CRect // 派生类
{
private:
CPoint low_right; // 注意low_right与up_left定义的顺序
CPoint up_left;
public:
int & CenterX;
const int CenterY;
CRect(int x1,int y1,int x2,int y2,int & x3,int y3)
:up_left(x1,y1),low_right(x2,y2),CenterX(x3),CenterY(y3) // 初始化行
{
cout<<"CRect初始化赋值部分"<<endl;
}
};
void main()
{
int cx=5;
int cy=6;
CRect R1(1,2,3,4,cx,cy);
cout<<"Center: x="<<R1.CenterX<<"y="<<R1.CenterY<<endl;
getch();
}
运行结果:
CPoint初始化赋值部分。x:3 y:4
CPoint初始化赋值部分。x:1 y:2
CRect初始化赋值部分
Center: x=5 y=6
需要说明的几点情况:
• 上面的例程对分层类(包括继承类)的初始化、常量成员变量的初始化和引用成员变量的初始化分别进行了说明。在CPoint类和CRect类的构造函数中都有信息输出,可以看出初始化行中的代码较赋值部分先执行。
• 对常量成员变量和引用成员变量进行初始化时,不要直接使用赋值符号,而是在变量后加一个括号,在其中指定参数。这类似于类的构造函数。
• 在对引用成员变量进行初始化的时候,构造函数参数列表中对应参数数据类型应该为引用类型。
• 初始化行中的顺序并不决定初始化的顺序。初始化的顺序是由类定义体中各变量和对象声明的顺序来决定的。比如,上面low_right在up_left前声明,所以初始化时low_right在前,尽管up_left在初始化行中是在前面。