跳至主要內容

特殊数据成员的初始化

张威大约 4 分钟c/c++类和对象

特殊数据成员的初始化

在C++的类中,有4种比较特殊的数据成员,他们分别是常量成员、引用成员、类对象成员和静态成员,他们的初始化与普通数据成员有所不同。

常量数据成员

当数据成员用const关键字进行修饰以后,就成为常量成员。一经初始化,该数据成员便具有“只读属 性”,在程序中无法对其值修改。事实上,在构造函数体内初始化const数据成员是非法的,它们只能在构造函数初始化列表中进行初始化。如:

class Point 
{
public:
    //错误写法
    Point(int ix = 0, int iy = 0)
    {
        _ix = ix;//error, 这是赋值语句,const成员不能修改
        _iy = iy;//error
        _iz = _ix;
    }
    
    //正确写法
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    , _iz(_ix)
    {
        
    }
    
private:
    const int _ix;	//常量数据成员,必须在初始化列表中进行
    const int _iy;
    int & _iz;
};

引用数据成员

和常量成员相同,引用成员也必须在构造函数初始化列表中进行初始化,否则编译报错。

#include <iostream>

using std::cout;
using std::endl;

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    , _ref(_ix)
    {
        cout << "Point(int = 0, int = 0)" << endl;
        /* _ix = ix;//error,赋值 */
        /* _iy = iy; */
    }

    void print()
    {
        cout << "(" << _ix << ", "
              << _iy << ")" <<  endl;
    }

    ~Point()
    {
        cout << "~Point()" << endl;
    }
private:
    int _ix;
    int _iy;
    int &_ref;
};

//成员函数不占用类的大小,成员函数存在于程序代码区,被该类的
//左右对象共享
//
int main(int argc, char **argv)
{
    cout << "sizeof(Point) = " << sizeof(Point) << endl;

    Point pt(1, 2);//栈对象
    return 0;
}
$./a.out 
sizeof(Point) = 16 #有内存对齐
Point(int = 0, int = 0)
~Point()

类对象成员

数据成员本身也是自定义类类型对象,比如一个直线类Line对象中包含两个Point类对象,对Point 对象的创建就必须要放在Line的构造函数的初始化列表中进行。如

#include <iostream>

using std::cout;
using std::endl;

class Point
{
public:
    /* Point(int ix = 0, int iy = 0) */
    Point(int ix, int iy)
    : _ix(ix)
    , _iy(iy)
    {
        cout << "Point(int = 0, int = 0)" << endl;
    }

    void print()
    {
        cout << "(" << _ix << ", "
              << _iy << ")" <<  endl;
    }

    ~Point()
    {
        cout << "~Point()" << endl;
    }
private:
    int _ix;
    int _iy;
};

class Line
{
public:
    Line(int x1, int y1, int x2, int y2)
    : _pt1(x1, y1)//类对象成员需要显示进行初始化,否则就是默认值
    , _pt2(x2, y2)
    {
        cout << "Line(int, int, int,int)" << endl;
    }

    void printLine()
    {
        _pt1.print();
        _pt2.print();
    }

    ~Line()
    {
        cout << "~Line()" << endl;
    }

private:
    Point _pt1;//类对象成员(子对象)
    Point _pt2;
};
int main(int argc, char **argv)
{
    Line line(1, 2, 3, 4);
    line.printLine();
    return 0;
}

当Line的构造函数没有在其初始化列表中初始化对象_pt1_pt2时,系统也会自动调用Point类的默认构造函数,此时就会与预期的构造不一致。因此需要显式在Line的构造函数初始化列表中初始化_pt1_pt2对象。

静态数据成员

C++允许使用static(静态存储)修饰数据成员,这样的成员在**就被创建并初始化的(与之相比,对象是在**被创建的),且其实例只有一个,被所有该类的对象共享,就像住在同一宿舍里的同学共享一个房间号一样。静态数据成员和之前介绍的静态变量一样,当程序执行时,该成员已经存在,一直到程序结束,任何该类对象都可对其进行访问,静态数据成员存储在全局/静态区,并不占据对象的存储空间。

class Computer
{
public:
    Computer(const char *brand, double price)
    : _brand(new char[strlen(brand) + 1]())
    , _price(price)
    {
        _totalPrice += _price;
    }
    
    void print()
    {
        cout << "品牌:" << _brand << endl
             << "价格:" << _price << endl
             << "总价:" << _totalPrice << endl;
    }
    //...
private:
    char * _brand;	
    double _price;	//4和 _brand 会有内存对齐
    static double _totalPrice;//4静态的数据成员不占类的大小,被类
                            //创建的所有对象所共享
};

因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类对象时被定义的。这意味着它们不是由类的的构造函数初始化的,一般来说,我们不能在类的内部初始化静态数据成员,必须在类的外部定义和初始化静态数据成员,且,格式如下:

类型 类名::变量名 = 初始化表达式; //普通变量
类型 类名::对象名(构造参数);     //对象变量
//静态数据成员要放在全局静态位置进行初始化,特别是对于头文件与
//实现文件的形式,要放在实现文件里面,否则会有多重定义的报错
double Computer::_totalPrice = 0;

注意:要放在里面,否则会有的报错