今天来分享一下C++中对象的初始化和清理。主要是介绍构造函数和析构函数,另外也会讲一下列表初始化和静态成员这些。 上面的例子是想说要做好初始化和善后工作。在C++中的对象也是如此。一个对象或者变量没有初始化拿去用,结果是未知的。使用完之后不去清理,也可能造成安全问题。 构造函数调用规则 默认情况下,c++编译器至少给一个类添加3个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝 简单的说,构造函数的调用规则就是 总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题 5、初始化列表 作用: C++提供了初始化列表语法,用来初始化属性 语法:构造函数():属性1(值1),属性2(值2).. public: static int m_A; //静态成员变量 //静态成员变量特点: //1 在编译阶段分配内存 //2 类内声明,类外初始化 //3 所有对象共享同一份数据
c++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁清理数据的设置。 1.构造函数和析构函数 对象的初始化和清理也是两个非常重要的安全问题: 一个对象或者变量没有初始状态,对其使用后结果未知。 同样的使用完一个对象或者变量,没有及时清理,也会造成一定的安全问题。 c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。 3.拷贝构造函数的调用时机 c++拷贝构造函数调用时有三种情况: 使用一个已经创建完毕的对象来初始化一个新对象; 值传递的方式给函数参数传输; 以值方式返回局部对象; 即这三种情况下都会调用拷贝构造函数 () { test3(); system("pause"); return 0; } 4.构造函数的调用规则 默认情况下,c++编译器至少会给一个类添加三个函数: 默认无参构造函数
C++初始化列表详解:性能优化与正确实践 在C++编程中,初始化列表是构造函数的重要组成部分,它不仅能提升代码性能,还能确保成员变量被正确初始化。本文将深入探讨初始化列表的语法、应用场景及最佳实践。 }; 3. C++对象生命周期的规则决定的。 引用(Reference) 引用的本质是对象的别名,一旦绑定到某个对象就无法重新绑定。因此: 必须在创建时初始化:引用没有“未初始化”状态,必须在定义时指定其引用的对象。 (val) {} // 正确:初始化列表中初始化 }; 3.
1.对象初始化器 首先声明一个类Person: public class Person { public string Name { get; set; } public int Age 可以支持对象初始化器的类型要满足一个条件是要有一个公有的无参的构造函数。 ::set_Name(string) 23 IL_0019: nop 24 IL_001a: ldloc.3 25 IL_001b: ldc.i4.s 22 26 //对象初始化器 这是比较宽松一点的情况下; 3.总结 可以发现集合初始化器和对象初始化器的共同点是它们都是编译器做的技巧。 和以前的写法产生的效果没有任何本质区别,但是集合初始化器产生的每个对象名我们就不知道了[编译器按照它的规则产生相应的对象名,我们无法直接引用]。
类中包含以下成员,必须放在初始化列表位置进行初始化: (1)引用成员变量 (2)const成员变量(刚刚有例子了) (3)自定义类型成员(且该类没有默认构造函数时) class A { public: C++:类与对象(2)-CSDN博客 去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。 3、注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。 3、对象传值返回优化 在func3中创建了一个变量,在拷贝一个临时变量返回,这个过程优化成直接构造。 传返回值拷贝构造了一次,又拷贝给aa1拷贝构造了一次,最后都被优化成直接构造。 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述
这一篇我们来给C++类与对象收尾,这一篇还会补充类的默认成员函数没讲的部分,开始吧 1.类的默认成员函数::取地址运算符重载 1.1const成员函数 C++将const修饰的成员函数称为 const成员函数 3.类型转换 C++支持内置类型隐式类型转换为 类 类型对象,需要相关内置类型为参数的构造函数 比如 A aa = 5 ; 则需要A类有一个参数为 int 的带参构造 构造函数前面加 explicit 3 通过A的构造函数 生成一个临时对象A ,然后A引用传参给Push函数(这个临时对象会在Push函数结束时被销毁,不用管),这就是隐式类型转换。 ,简化调用方式(如果不static修饰,想调用就得再实例化一个对象来间接调用,加了只需要指定类域A::Get_count),是 C++ 中 “静态成员配静态接口” 的经典设计范式。 综上,析构顺序是 BADC 所以答案选择 B C++类与对象(3)到此结束。下一篇我将结束类与对象的知识,请大家多多支持,有误请指出,谢谢大家!
我们可以调用不同的函数,如果只是进行的读取,我们可以调用上面的函数,这样的话我们是没有办法对数据进行修改的;如果想要对数据进行修改,我们就可以调用下面的函数,这样面对不同的情况,调用不同的函数,效率更高; 3. 关于取地址相关的默认构造函数 实际上构造函数除了默认的构造,析构,拷贝构造函数,还有一类构造函数是和取地址相关的; 就是取地址的符号原本也是一个运算符,如果我们直接对一个对象进行使用,本来应该是进行运算符的重载的 ,但是实际上我们使用的时候却不会报错,这个就是因为我们的运算符有一个默认的函数,我们可以直接获取某个对象的地址,不需要进行运算符的重载; 4.流插入&&流提取的运算符重载 (1)流插入运算符和流提取运算符是 C++里面引入的两个秘密武器,他解决了c语言里面的printf和scanf的局限性问题; (2)如果我们想要打印cout<<d1这个对象,显然是无法实现的,但是我们可以对流插入和流提取这两个运算符进行重载 ; (3)这里我们考虑的更加全面,我们这里的重载适合cout<<d1<<d2这样多次进行输出的情况,因此我们设置了返回值; (4)这里的重载不能设置为成员函数,因为成员函数里面有默认的this指针,但是我们实际输出的时候
C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。 4. 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。 Date d(2024, 7, 24); /* // 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造 Date d1(&d); d1.print(); //这样写才是拷⻉构造,通过同类型的对象初始化构造 0; } 补充: 在C++中,当通过值传递自定义类型的对象时,确实会调用拷贝构造函数进行对象的拷贝。 C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。 ,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象。
如果你没有给缺省值,对于没 有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。 类型转换C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。构造函数前面加explicit就不再支持隐式类型转换。类类型的对象之间也可以隐式转换,需要相应的构造函数支持。 :// <2> C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数class A{public:A(int a1):_a1(a1){}private:int _a1 = 1; ,要加constfunc(1);func(a1);func(); // 也可以给缺省值Stack st1;A a3(3);st1.Push(a3);st1.Push(3); // 3能够通过构造函数转换为一个 对象拷贝时的编译器优化现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返回值的过程中可以省略的拷贝。如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。
2>无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化。 2. 类型转换 c++支持内置类型隐式类型转换为类类型对象,需要有内置类型为参数的构造函数。 ,在用这个对象拷贝构造aa1 //编译器将连续构造+拷贝构造优化为直接构造 A aa3 = { 2,2 }; B b = aa3; // aa3隐式类型转换为b return 0; } 3. 静态成员变量不能在声明位置给缺省值,因为缺省值是给初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。 4. int _a2 = 2; }; class B { //友元声明 friend void func(const A& aa, const B& bb); private: int _b1 = 3; 匿名对象 用类型(实参)定义出来的对象叫做匿名对象。 匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可时可以用匿名对象。
引用类型与值类型的初始化差异 这是一个关于C++中引用类型和值类型初始化的核心概念问题。 内存模型差异 引用不占用额外存储空间(通常实现为指针,但语义上不是指针) 对象需要分配实际的内存空间来存储数据 3. 总结 引用必须初始化:因为引用是别名,必须指向一个已存在的对象 对象可不初始化:对象可以默认构造或稍后赋值 引用成员必须在构造函数的初始化列表中初始化 对象成员可以在初始化列表中初始化,也可以在构造函数体中赋值 这种差异是C++语言设计的一部分,确保了类型安全和语义清晰。 理解这一区别对于编写正确和高效的C++代码至关重要。
C++ 编译器 发现 使用 匿名对象 时 , 会根据 匿名对象 的用法 , 决定对 匿名对象的 处理 ; 匿名对象单独使用 : 如果只是单纯的使用 匿名对象 , 没有涉及到 将 匿名对象 赋值给其它变量 , 就会在表达式执行完毕后 , 销毁匿名对象 ; 使用匿名对象初始化变量 : 如果 创建 匿名对象 后 , 还使用 匿名对象 初始化 变量 , 此时 编译器 会将 匿名对象 转为 普通对象 , 不会销毁该匿名对象 , 并且立刻销毁该匿名对象 ; 一、将 " 匿名对象 " 初始化给变量 1、使用匿名对象进行初始化操作 " 匿名对象 " 的 作用域 仅限于其所在的 表达式 , 这句表达式 执行完毕后 , 匿名对象 自动销毁 s = Student(12, 170); C++ 编译器识别到上述操作后 , 会将 匿名对象 转为 变量名为 s 的 实例对象 ; 此时 即使该语句 执行完毕 , 创建的 匿名对象 , 被转换为普通对象 , 自然就不会被销毁 ; 这里 将 " 匿名对象 " 直接转为 " 普通对象 " , 这里只是进行单纯的转换 , 不涉及拷贝复制的情况 ; 3、代码示例 - 将 " 匿名对象 " 赋值给变量 代码示例
类与对象 1 再谈构造函数 1.1 构造函数体赋值 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值,以我们之前实现的Date类对象为例。 ,但是不能将其成为对象中成员变量的初始化,构造函数中语句只能将其成为赋初值,不能叫做初始化。 1.3 explicit关键字 构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。 // 实际编译器背后会用2023构造一个无名对象, //最后用无名对象给d1对象进行赋值 } class Date { public: // 2. 虽然静态成员不属于类的某个对象,但是我们依然可以使用类的对象、引用、或者指针来访问静态成员。
前言 在前面的博客中已经分享有关构造函数 【C++】构造函数和析构函数详解,这次又再一次提到构造函数,一起来看看。 2. ,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。 这里_n没有办法初始化,它只能在函数体。 声明并没有定义,是在对象实例化的时候才整体定义。 但是有一些成员必须在定义的时候初始化。 所以c++中用了初始化列表,初始化列表是每个成员变量定义初始化的位置。 下面的成员变量也会走初始化列表,他们也要定义,只是没有给值就是随机值,如果给了值就直接初始化。 (2)const成员变量 (3)自定义类型成员(且该类没有默认构造函数时) 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
1 流重载 C语言中printf和scanf是有局限性,只能直接打印内置类型,对于自定义类型就哦豁了,所以在C++中就引用了流的概念,也就是cou cin: 为什么说打印输出的时候不需要占位符,这就是因为流就是一个重载了的函数 ,所以每次打印的时候都会调用对应的重载函数,比如多次打印的时候,printf一下就打印出来了,但是对于流不行,它要调用许多次重载函数,才能打印出,这也就导致了C++效率不如C语言高,对于不同类型,都可以进行打印 ,那么我们显式实现一下流重载: 首先我们要知道cout和cin来源于io流,作为一个全局对象出现的,所以cin的类型是istream,cout是ostream的,所以我们有一个参数的类型一定是ostream cout << "const A* operator&" << endl; return this; } private: int _a = 1; int _b = 2; int _c = 3; 类和对象中就结束了,终于结束了,挺多的, 感谢阅读!
C#对象初始化 之前在学习过程中只是知道该如何初始化对象,但是却不明白为何要这么做,不这么做有什么问题。 初始化定义: 初始化在计算机编程领域中指为数据对象或变量赋初值的做法,如何初始化则取决于所用的程序语言以及所要初始化的对象的存储类型等属性。用于进行初始化的程序结构则称为初始化器或初始化列表。 1、 在 栈内存为引用开辟空间 2、 在 堆内存为对象开辟空间 3、 对 对象的成员变量进行 默认初始化 //默认初始化为null 4、 对 对象的成员变量进行 显示初始化 //赋予初始值 5、 通过 构造方法对 对象的成员 变量赋值 6、 对象初始化完毕,把对象地址赋值给引用变量 二 、变量声明后和变量赋值为null或变量调用了new的区别。 3、变量调用new 变量中存在了对象也就是存在了字段,只是字段进行了赋值,赋值为默认值。
❝在C++中int类型可以看作为一个类,那么它就有以下的初始化方式。 ❞ int i; /* 默认初始化 */ int i = 0; /* 拷贝初始化 */ int i(0); /* 直接初始化 */ int *i = new int(); /* 值初始化 */ int i{0}; /* 列表初始化 */ std::vector<int> i{1, 2, 3}; /* 列表初始化 */
ES.20: Always initialize an object ES.20: 保证所有对象被初始化 Reason(原因) Avoid used-before-set errors and their The latter, more relaxed rule, catches the technical bugs, but: 保证对象被初始化原则显然高于对象在使用前必须被设置原则。 确保对象初始化原则是一条致力于提高维护性的风格规则,也是可以防止设定之前使用的规则。 由于cm3是一个常数,编译器会提示它没有被初始化,但是m3没有被初始化这件事会被漏掉。通常,一个很少用到的伪成员初始化可以避免初始化遗漏并且通常优化器可以去掉多余的初始化。 beyond the input -- and that has been a fertile source of errors and security breaches: 如果你声明一个只希望根据输入信息初始化的对象
关键字使用 3.结语 1.初始化列表 1.1初始化列表定义 C++中的初始化列表是一种在对象构造函数中初始化成员变量的方法。 ,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。 1.2初始化列表原因 在C++类和对象中有些成员变量必须定义的时候初始化,这时候如果只是简单的使用构造函数来赋值是不可行的,所以C++引入了初始化列表这个概念; 类中包含以下成员,必须放在初始化列表位置进行初始化 ,因为常量成员变量在对象创建后不能修改; 引用成员变量 _ref 必须在构造函数的初始化列表中进行初始化,因为引用成员变量在创建后不能修改绑定的对象。 3.结语 初始化列表是C++类和对象中初始化成员变量的方式,在一些情况下可以提高效率和代码可读性。
对象生命周期 2.2. 不一定需要显式析构 2.3. 析构的必要性 3. 总结 1. 对象生命周期 在经典C++中,需要通过new/delete来手动管理动态内存。 这也体现了前文《面向对象编程(C++篇1)——引言》中提到的设计原则:类是抽象的自定义数据类型。 2.2. 因为现代C++的一些机制能够帮你自动管理动态内存。但是析构函数还是必要的,这是由于C++语言本身的性质决定的。作为C语言大部分内容的超集,需要兼容C语言手动管理内存的特性。 3. 总结 所以我们就能理解了,C++这门语言的设计哲学就是就是这样:既想要C语言的高性能,也想要高级语言高度抽象的特性。