当前位置: 首页 > news >正文

C++类:特殊的数据成员

目录

特殊的数据成员

常量数据成员

引用数据成员

对象成员

​编辑

构造顺序的强制性

静态数据成员


特殊的数据成员

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

常量数据成员

当数据成员用 const 关键字进行修饰以后,就成为常量成员。一经初始化,该数据成员便具有“只读属性”,在程序中无法对其值修改。事实上,在构造函数体内对const 数据成员赋值是非法的,const数据成员需在初始化列表中进行初始化(C++11之后也允许在声明时就初始化)。

普通的const常量必须在声明时就初始化,初始化之后就不再允许修改值;

const成员初始化后也不再允许修改值。

class Point {
public:
    Point(int ix, int iy)
    : _ix(ix) //初始化列表本质就是进行初始化的
    , _iy(iy)
    {}
private:
    const int _ix;
    const int _iy;
};

image-20240308114326646

引用数据成员

引用数据成员在初始化列表中进行初始化,C++11之后允许在声明时初始化(绑定)。

之前的学习中,我们知道了引用要绑定到已经存在的变量,引用成员同样如此。

class Point {
public:
    Point(int ix, int iy)
    : _ix(ix)
    , _iy(iy)
    , _iz(_ix)
    {}
private:
    const int _ix;
    const int _iy;
    int & _iz;
};

思考:构造函数再接收一个参数,用这个参数初始化引用成员可以吗?

image-20240308120118431

_iz绑定传入的z,看起来虽然是确定的值,但是由于值传递会进行复制,所以实际上是去绑定一个临时变量,临时变量的生命周期只有函数,等到构造函数结束引用的指向就没了,就成为了一个悬空指针。

_iz绑定 _ix,因为数据成员的初始化顺序与声明顺序一致,此时 _ix已经完成了初始化,是一个确定的值,就没有问题。

我现在好奇Point 的大小是多少,我们来看一下,发现为16字节,证明&引用还是占用空间的,并且是一个指针的内存大小。

对象成员

有时候,一个类对象会作为另一个类对象的数据成员被使用。比如一个直线类Line对象中包含两个Point对象。

对象成员必须在初始化列表中进行初始化。

注意:

  1. 不能在声明对象成员时就去创建。

  2. 初始化列表中写的是需要被初始化的对象成员的名称,而不是对象成员的类名。

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;
    }
private:
    Point _pt1;
    Point _pt2;
};

image-20240308144217447

注意:

如果在Line类的构造函数的初始化列表中没有显式地初始化Point类对象成员,编译器会自动去调用Point类型的默认无参构造;

如果不想用Point的无参构造,那么必须在Line类的初始化列表中对Point类的对象成员进行初始化

image-20240308144444067

构造顺序的强制性
  • 当类的对象包含其他类的对象作为成员时,这些成员对象的构造函数会在主类的构造函数体执行前自动调用

  • 如果未在初始化列表中显式指定成员对象的构造函数,编译器会尝试调用它们的默认构造函数(无参构造函数)。若成员对象的类没有默认构造函数,会导致编译错误

  • class Point {
    public:
        Point(int x, int y); // 没有默认构造函数
    };
    
    class Line {
        Point p1; // 必须通过初始化列表构造
        Point p2;
    public:
        Line(int x1, int y1, int x2, int y2) 
            : p1(x1, y1), p2(x2, y2) {} // 正确:显式调用有参构造函数
    };

  • 如果省略初始化列表,编译器会报错,因为Point没有默认构造函数。

此例子中,创建一个Line类的对象,会首先调用Line的构造函数,在此过程中调用Point的构造函数完成Point类对象成员的初始化;

Line对象销毁时会先调用Line的析构函数,析构函数执行完后,再调用Point的析构函数。(对象销毁系统才会自动调用析构函数)

image-20240308145237461

—— 与看起来的顺序有所不同。

静态数据成员

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

静态数据成员被整个类的所有对象共享。

class Computer {
public:
    //...    
private:
    char * _brand;
    double _price;
    //数据成员的类型前面加上static关键字
    //表示这是一个static数据成员(共享)
    static double _totalPrice;
};
double Computer::_totalPrice = 0;

静态成员规则:

  1. private的静态数据成员无法在类之外直接访问(显然)

  2. 对于静态数据成员的初始化,必须放在类外(一般紧接着类的定义,这是规则1的特殊情况),因为静态成员初始化一定要放在类外初始化,类外的空间有很多,所以编译器一般要求创建完类之后,紧接着对静态数据成员初始化。不能在类内进行初始化是因为他被所有该类对象所共享,如果在类内初始化就会每创建一个对象就会变化。

  3. 静态数据成员初始化时不能在数据类型前面加static,在数据成员名前面要加上类名+作用域限定符

  4. 如果有多条静态数据成员,那么它们的初始化顺序需要与声明顺序一致(规范)

image-20240308150638895

image-20240308151253143

相关文章:

  • Linux环境使用jmeter做性能测试
  • 全球化2.0 | ZStack云计算系统工程师(ZCCE)国际认证培训成功举办
  • win10 c++ VsCode 配置PCL open3d并显示
  • 猎豹移动(Cheetah Mobile)
  • 【Unity】TextMesh Pro显示中文部分字体异常
  • 基于FPGA的3U机箱模拟量高速采样板ADI板卡,应用于轨道交通/电力储能等
  • 游戏引擎学习第157天
  • LeRobot源码剖析——对机器人各个动作策略的统一封装:包含ALOHA ACT、Diffusion Policy、VLA模型π0
  • 关于微信小程序端base64解码问题
  • 【Spring】声明式事务传播机制
  • 【Go】go语言指针
  • 表达式引擎之通用的开屏页后端接口设计
  • 需要使用新应用以打开此ms-gamingoverlay链接怎么解决
  • 【FPGA】——实现六位流水灯
  • Oracle底层原理解析
  • JS | JS中的日期和时间的获取方法
  • 二阶近似 是什么意思
  • Web开发-PHP应用文件操作安全上传下载任意读取删除目录遍历文件包含
  • 字符串p型编码(信息学奥赛一本通-1145)
  • 理解我们单片机拥有的资源
  • 商务部新闻发言人就波音公司飞回拟交付飞机答记者问
  • 黄仁勋访华期间表示希望继续与中国合作,贸促会回应
  • 幸福航空取消“五一”前航班,财务人员透露“没钱飞了”
  • 在差异中建共鸣,《20世纪美国文学思想研究》丛书出版
  • 利物浦提前四轮英超夺冠,顶级联赛冠军数追平曼联
  • 凝聚多方力量,中国农科院油菜产业专家团部署单产提升新任务