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

C++创建对象过程

参考:

c++创建对象过程详解 - LEO__Y - 博客园 (cnblogs.com)

创建对象的过程

1.分配内存空间

2.初始化成员变量

3.调用构造方法

内存分配(Allocation)

全局对象和静态对象

编译器会为他们划分一个独立的段(全局段)为他们分配足够的空间

栈对象

MyClass obj; // 栈上分配

编译器在栈帧中直接预留空间,大小由 sizeof(MyClass) 决定。

分配速度极快(只需移动栈指针),无需手动释放。

堆对象

MyClass* obj = new MyClass(); // 堆上分配

调用 operator new 在堆上分配内存(底层可能调用 malloc)。

需要手动 delete 释放,否则内存泄漏。

注意:对象在栈或者堆上整体分配的,那么类里面的成员就在哪个位置存着,对象是局部的,那么成员就是局部的,对象是全局的,那么成员就是全局的。当然,不包括静态成员,因为静态成员不属于对象,而是属于类的,所以会在创建对象之前,程序启动时就初始化,后续详细说明。

在看到C++的类成员被调用时,比如一个类的成员变量被调用,我总是会下意识地去想这个到底是局部变量还是全局变量,这是C语言的惯性思维,而在C++中,成员是随着对象的位置而存在的,对象在哪片内存上,成员就在哪片内存上,访问成员,都需要通过对象来调用,当然,除了静态对象。

记住,类本质上只是一种数据和函数的组织形式。

其实类就跟结构体一样,是个声明而已,只有创建对应实例的时候才能使用(除了静态成员)

初始化成员变量

成员变量的初始化发生在构造函数体执行之前,具体分为两个阶段:

  1. 在进入构造函数体之前,所有成员变量会先进行默认初始化(类类型调用默认构造函数)。
  2. 如果使用了成员初始化列表(member initializer list),则会用指定的值覆盖默认初始化的结果。

初始化 (Initialization)和赋值(Assignment)。初始化早于赋值,它是随着对象的诞生一起进行的。而赋值是在对象诞生以后又给予它一个新的值。对象经过初始化以后,我们仍然可以对其进行赋值。我们可以通过构造函数的实现体(即构造函数中由"{}"包裹的部分)来实现。

注意:如果使用了构造函数的初始化列表,则会在初始化时直接就赋予对应的值,而不必分成初始化和赋值两个步骤了。


默认初始化的规则

内置类型(如intdouble、指针等):

如果未显式初始化,值是未定义的(除非是全局变量或static成员,会初始化为0)。

类类型:

调用默认构造函数初始化。如果没有默认构造函数且未在初始化列表中显式初始化,则编译错误。


初始化方式

(1) 成员初始化列表(推荐)

class Example {
public:Example(int x, int y) : a(x), b(y) { // 构造函数体(此时a和b已初始化)}
private:int a;int b;
};

时机:在构造函数体执行之前完成初始化。

特点:

直接调用成员的构造函数或赋值,效率更高(避免默认初始化 + 赋值的双重操作)。

const成员、引用成员、没有默认构造函数的类成员必须用初始化列表。

当类的成员变量是const 或者 引用类型时,必须使用初始化列表进行初始化,原因是:

  1. const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

  2. const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt

  3. 引用的指向只能初始化,不能修改其指向,赋值过程是在修改其指向,其实这就是赋值和初始化的本质区别

(2) 构造函数体内赋值

class Example {
public:Example(int x, int y) {a = x; // 赋值,非初始化b = y; // 赋值,非初始化}
private:int a;int b;
};

时机:在构造函数体内赋值时,成员变量已经经历了默认初始化(内置类型如int是未定义值,类类型调用默认构造函数)。

缺点:效率较低(尤其是类类型成员,会先默认构造再赋值)。


特殊情况

静态成员变量(static):

必须在类外单独定义和初始化(不占用对象内存)。

class Example {static int count;
};
int Example::count = 0; // 必须在类外初始化

常量静态成员(const static):

class Example {const static int MAX = 100;
};

C++11 后的类内初始化

C++11允许在类定义中直接为成员变量赋默认值:

class Example {int a = 10;     // 类内初始化std::string s = "Hello";
public:Example() {}     // a和s会以10和"Hello"初始化
};

初始化时机:如果构造函数未在初始化列表中显式指定值,则使用类内初始值(本质上等同于编译器将类内初始化放到初始化列表中)。


为什么基本类型成员默认不初始化为0?

性能考虑:C++的设计哲学是“不为不需要的操作付出代价”。强制初始化为零可能影响性能(尤其是频繁创建对象的场景)。

明确性:要求程序员显式初始化,避免隐藏的错误(如误用未初始化的值)。

通过显式初始化成员变量,可以确保代码的可预测性和安全性。

更多待补充。

http://www.xdnf.cn/news/721783.html

相关文章:

  • 攻防世界-BadProgrammer
  • siglip2(2) Naflex模型的动态分辨率原理
  • 微信小店推客系统带来的便利性
  • IPTV电视直播 1.6.0 | 手机电视直播 秒播无卡顿
  • 短视频一键搬运 v1.7.1|短视频无水印下载 一键去重
  • 计算几何 视频截图
  • int和Integer的区别
  • vue3+element plus 关于el-dialog__body无法选中的问题
  • 掌握STP技术:网络环路终结者实战
  • cf2067A
  • 定位例子(vue3)
  • 告别RAG上下文丢失:Late Chunking 与 Contextual Retrieval 深度对比解析
  • 【实证分析】上市公司全要素生产率+5种测算方式(1999-2024年)
  • OTA中版本灰度发布、用户反馈闭环浅谈
  • 深度解构:Profinet转Profibus网关如何重塑产品分离装置的控制逻辑
  • 【测试】设计测试⽤例方法
  • 键盘录入的两套体系区别(Random)
  • 【速通RAG实战:进阶】16、AI生成思维导图全技术解析
  • SpringBoot(五)--- 异常处理、JWT令牌、拦截技术
  • python的高级2——函数作为对象
  • ⚽【足球数据全维度解析】从基础统计到高阶分析,数据如何重塑现代足球?
  • 中国国运新引擎:下一代液晶技术突破如何重塑全球显示格局
  • 通过粘性布局实现表格且带有固定列
  • 文件夹的命名与分类
  • Geoserver修行记--点击geoserver服务的WMTS能力(GetCapabilities)文档显示400 null
  • 第五十九节:性能优化-GPU加速 (CUDA 模块)
  • 2025-5-27Vue3快速上手
  • 软考-系统架构设计师-第八章 数据库设计基础知识
  • Lesson 25 Do the English speak English
  • DMBOK对比知识点对比(1)