大庆+网站建设网站 宽屏窄屏自适应

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

大庆+网站建设,网站 宽屏窄屏自适应,外贸公司应该怎样做外贸网站,wordpress+高性能深入篇【C】类与对象#xff1a;再谈构造函数之初始化列表与explicit关键字 Ⅰ.再谈构造函数①.构造函数体赋值②.初始化列表赋值【特性分析】1.至多性2.特殊成员必在性3.必走性#xff1a;定义位置4.一致性5.不足性 Ⅱ.explicit关键字①.隐式类型转化②.作用 Ⅰ.再谈… 深入篇【C】类与对象再谈构造函数之初始化列表与explicit关键字 Ⅰ.再谈构造函数①.构造函数体赋值②.初始化列表赋值【特性分析】1.至多性2.特殊成员必在性3.必走性定义位置4.一致性5.不足性 Ⅱ.explicit关键字①.隐式类型转化②.作用 Ⅰ.再谈构造函数 我们知道在创建对象时编译器会通过调用构造函数给对象中各个成员变量一个合适的初始值。 也就是我们可以通过构造函数来给对象中的成员变量赋值。不过给成员变量赋值其实有两种方式一种就是在函数体内进行赋值另一种是在初始化列表赋值。函数体内赋值我们是知道什么意思那什么叫初始化列表呢 我们知道创建一个对象什么表示对象创建出来了呢 对象实例化表示对象已经创建出来这是对象整体定义的地方然后对象就会调用构造函数进行初始化。 对象定义的地方是对象实例化实例化后就会调用构造函数初始化。 那想一想对象成员变量是在哪里定义的呢 对象实例化只是对对象整体定义的地方而初始化列表才是对象成员变量定义的地方。 只有定义完后才可以初始化。 所以初始化列表是对象成员定义的地方。 ①.构造函数体赋值 对象实例化后就会调用构造函数初始化对象。 在函数体内部进行赋值初始化。 handlebarsclass Date { public:Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}void Print()const{cout _year - _month - _day endl;} private:int _year;int _month;int _day; };虽然上述构造函数调用之后对象种已经有一个初始值但是不能将其称为对对象中成员变量的初始化构造函数体中的语句只能将其称为赋初值而不能称作初始化。 因为初始化只能初始化一次但构造函数体内可以多次赋值。 本质上来说是因为对象成员变量不在函数体内定义所以赋值后也不叫作初始化只有在定义的地方赋值才可以叫做初始化而对象成员变量定义的地方其实是初始化列表。 ②.初始化列表赋值 初始化列表以一个冒号开始接着就是以一个逗号分割数据成员列表每个成员变量后面跟上一个括号括号里是初始值或者表达式。 class Date { public:Date(int year 1, int month 1, int day 1)//这个就叫做初始化列表:_year(year)//以一个冒号开始注意后面没有分号,_month(month)//逗号分割,_day(day)//每个成员变量后面都有一个括号括号里是初始值或表达式{} private:int _year;int _month;int _day; };【特性分析】 1.至多性 每个成员变量在初始化列表中至多出现一次也可以不出现。 因为定义完后再初始化而初始化只能初始化一次。 不出现的话那就会在函数体内进行初始化。 2.特殊成员必在性 类中包含以下成员时必须放在初始化列表位置进行初始化。 引用成员变量const成员变量自定义类型成员且该类没有默认构造函数时 我们一个一个分析为什么上面三个成员必须放在初始化列表初始化。 引用成员变量和const成员变量有什么特别之处呢为什么会被要求放在初始化列表初始化呢 引用成员变量和const成员变量都有一个特点那就是在定义的时候必须初始化。 不然编译器会报错而初始化列表正是变量定义的地方在定义的地方给初始值才能成功的对引用成员变量和const成员变量初始化。如果在函数体内部进行赋值初始值那这样不是初始化因为函数体内部不是它们定义的地方仅仅给个赋值是不能完成初始化的。 class B { public:B(int a, int ref)//初始化列表成员变量定义的地方:_ref(ref)//引用, _n(1)//const修饰的{} private:int _ref;//引用成员变量//这两个特征就是必须在定义的时候就初始化const int _n;//const修饰的成员变量 };第三种成员变量是自定义类型成员并且当类中没有默认构造函数时自定义类型成员必须放在初始化列表初始化这是为什么呢 class A { public:A(int a0 )//有默认构造函数:_a(a){cout A(int a 0) endl;} private:int _a; }; class B { public://初始化列表对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的{} private:A _obj;//有默认构造函数时可以不用初始化int _ref;const int _n;int _x 1; };我们知道编译器生成的默认构造的工作是对自定义类型初始化对内置类型不做处理。 所以当有默认构造函数时自定义类型我们就不用去再初始化了。因为没有参数我也可以调用构造函数初始化。那没有默认构造函数呢我们是不是就得手动给自定义类型成员变量进行初始化。但要注意的是函数体内不允许没有默认构造的自定义类型成员变量初始化必须在初始化列表初始化。 道理其实是一样的自定义类型在定义的时候也要进行初始化。 那怎么初始化呢 —调用构造函数。 如果有默认构造函数那就可以之间使用默认构造函数初始化。 如果没有默认构造函数那就必须在初始化列表进行初始化因为初始化列表是成员变量定义的地方如果要求在定义的时候进行初始化那么必须得在初始化列表进行函数体内部不是定义的地方只是可以进行赋初始值如果在函数体内部进行初始化其实是定义和赋初始值分开了没有做到定义只是进行赋初始值。 而对于那些没有要求在定义时必须初始化的变量既可以在初始化列表进行初始化也可以在函数体内部进行初始化可以做到定义和赋初始值分开。 class A { public:A(int a )//这个不是默认构造这个是需要传参的构造函数:_a(a){cout A(int a 0) endl;} private:int _a; }; class B { public://初始化列表对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a)//自定义类型无默认构造。{} private:A _obj;//没有默认构造函数时必须在初始化列表进行初始化。int _ref;//引用const int _n;//const修饰的int _x 1;};要区分默认成员函数和默认构造函数 1.默认成员函数是C规定的几种特殊的成员函数是不写编译器可以自动生成的有默认构造函数默认拷贝函数默认赋值函数等等。而默认构造函数是包含在内的。 2.默认构造函数有三种总的特征就是不用传参就可以使用的函数。 无参的构造函数全缺省的构造函数编译器生成的默认构造函数都叫做默认构造函数。 3.并且默认构造函数只能有一个。 所以当自定义类型成员变量类型没有默认构造函数时(三种默认构造函数自己写的带有参数构造函数)在函数体内部赋初始值是无法通过的必须在初始化列表显式初始化去调用自己写的构造函数初始化。 也就是当没有提供默认构造函数的自定义类型必须在初始化列表初始化。 3.必走性定义位置 尽量使用初始化列表初始化为什么呢 因为不管你是否使用初始化列表对于自定义类型成员变量一定会显示有初始化列表初始化并且所有成员变量都会经过初始化列表的。因为初始化列表是成员变量定义的地方。 每个成员变量在初始化列表至多出现一次但也可以不出现不出现的意思是不给定义的变量赋初始值但这个变量是在初始化列表定义的只不过没有给初始值其实所有的成员变量都会走初始化列表。 class B { public://初始化列表对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a),_x(2)//对于那些没有要求必须在定义时初始化的既可以在初始化列表初始化{//_x2;//也可以在函数体内部初始化} private:A _obj;int _ref;const int _n;int _x ;//初始化列表没有显式定义_x就会使用这个缺省值 };比如上面的内置类型_x既可以在初始化列表初始化又可以在函数体内部初始化在初始化列表初始化就是定义时就初始化了而在函数体内部初始化就是在初始化列表定义完后到函数体内赋初始值完成初始化。 class B { public://初始化列表对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a){} private:A _obj;int _ref;const int _n;int _x1 ;//这个1是缺省值缺省值是给初始化列表的 你们还记得缺省值吧C给默认构造函数打补丁就规定了可以在成员变量声明时给缺省时这样对于内置类型也可以完成初始化了那现在看来这个缺省值是如何完成初始化的呢 其实这个缺省值就是给初始化列表用的因为每个成员变量都会走初始化列表初始化列表会将缺省值赋值给已经定义好的成员变量这样成员变量就完成了初始化了。 当显式的初始化时初始化列表会优先选取显式给的初始值而放弃掉缺省值当没有显式的初始化时初始化列表就会将缺省值赋值给成员变量 。 4.一致性 成员变量在类中声明次序就是在其初始化列表中的初始化顺序。 声明的顺序对应着要初始化的顺序必须要一致不然会出现问题。 比如下面这个问题 class A { public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;} private:int _a2;int _a1; }; int main() {A aa(1);aa.Print(); }这个结果是什么呢 为什么呢注意到成员变量声明的次序_a2先声明_a1后声明则表明_a2先进行初始化_a1后进行初始化
5.不足性 初始化列表的使用虽然很方便但也有它不足之处比如当有些赋值后的成员需要检查是否赋值成功再比如要求对数组进行初始化初始化列表就无法完成这样的工作。 class Stack { public:Stack(int capacity10):_a((int)malloc(sizeof(int) capacity)),_top(0),_capacity(capacity){if (_a nullptr){perror(malloc);//要求数组初始化一下memset(_a, 0, sizeof(int) * capacity);}} private:int* _a;int _top;int _capacity };再比如写一个动态二维数组初始化列表就无法完成这样的工作必须借助函数体来解决所以总有一些工作是初始化列表完成不了的这时就需要和函数体一起协同工作了。 class AA { public:AA(int row 10, int col 5):_row(row), _col(col){_a (int)malloc(sizeof(int*) * row);for (int i 0; i row; i){_aimalloc(sizeof(int) * col);}} private:int _a;int _row;int _col; };Ⅱ.explicit关键字 ①.隐式类型转化 构造函数不仅可以构造与初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数还具有类型转化的作用。 这其实就是隐式类型转化看下面代码 int main() {int i 0;double d1 i;//这里发生了什么呢 }这里i是int类型d1是double类型将i赋值给d1会发生类型转化。发生什么样子的转化呢 其实在这个过程中会生成一个临时变量将i赋给临时变量临时变量再赋给d1.
class A { public:A(int a):_a(a){}A(const A a1){_a a1._a;} private:int _a; }; int main() {A aa1(1);A aa2 4; }对象aa1调用构造函数初始化成1那对象aa2在干嘛呢 对象aa2其实是将整形的4赋给aa2这里就涉及了隐式类型转化将int类型隐式转换成A类型。 这里其实是4调用构造函数构造一个A类型的临时对象然后临时对象再拷贝构造给aa2。只不过编译器将这两步优化成一步优化成直接构造。 如果不相信的话可以看看下面的代码 int main() {A aa1(1);A aa2 4; }用对象aa2来引用4可以吗 这里肯定不行呀这两个类型都不一样肯定不能引用。 但只要给对象aa2前面加上const修饰那么这样就可以引用了这是为什么呢
不知道你还记得临时变量具有常性这个特点吗 正是因为这个特点加上const修饰后就允许引用了因为4会调用构造函数构造出一个A类型的临时变量而因为临时变量具有常性相比较正常的A类型权限缩小了所以不能相互引用但一旦加上const修饰后权限相同就可以相互引用了。 所以在隐式类型转化时会产生一个临时变量的。 ②.作用 如果不想在调用构造函数时构造出一个临时变量的话就可以用关键字explicit。 使用explicit后构造函数就无法构造出临时变量了这样的隐式类型转化就不会发生了。 class A { public:explicit A(int a):_a(a){}A(const A a1){_a a1._a;} private:int _a; }; int main() {A aa1(1);A aa2 4; }