海口市做网站的公司,上海网站建设怎么弄,网站模板大全,html网页制作成品CSDN的uu们#xff0c;大家好。这里是C入门的第十一讲。 座右铭#xff1a;前路坎坷#xff0c;披荆斩棘#xff0c;扶摇直上。 博客主页#xff1a; 姬如祎 收录专栏#xff1a;C专题 目录
1. 初始化列表
1.1 引入
1.2 初始化列表
1.3 初始化列表的注意事项
1.… · CSDN的uu们大家好。这里是C入门的第十一讲。 · 座右铭前路坎坷披荆斩棘扶摇直上。 · 博客主页 姬如祎 · 收录专栏C专题 目录
1. 初始化列表
1.1 引入
1.2 初始化列表
1.3 初始化列表的注意事项
1.4 初始化列表成员变量的初始化顺序
2. 同一表达式连续构造的优化
3. 静态成员变量与静态成员函数
4. 内部类 1. 初始化列表
1.1 引入
还记得我们之前在学习类和对象的时候是怎么给类的成员变量 初始化 的吗我们是通过构造函数在函数体内部给成员变量赋值的。为什么说是赋值而不是说初始化呢因为成员变量的初始化就不是在函数体内部完成的而是一个叫初始化列表的东西中完成的
我们定义了一个类A在构造函数中我们对成员变量进行了多次赋值以此来证明构造函数的函数体不是对成员变量进行初始化的地方。
class A
{
public:A(int x 0, int y 0){_x x;_x 5;_y y;}
private:int _x;int _y;
};int main()
{A a(2, 3);return 0;
}
1.2 初始化列表
我们先来看看初始化列表的语法吧 以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。 比如下面的代码
class A
{
public:A(int x 0, int y 0):_x(x),_y(y x){}
private:int _x;int _y;
};
在括号里面也是可以写表达式的哦
我们的C祖师爷为什么要设计出初始化列表呢那肯定是因为函数体内的赋值无法对所有类型的成员变量进行赋值。我们来看下面的代码 我们看到类的成员变量如果有const 修饰的变量或者引用在构造函数的函数体内部赋值就会直接报错了根据报错提示我们也能够隐隐猜到构造函数的函数体不是成员变量初始化的地方
对于const修饰的变量 和引用都有一个共同的特性在定义的时候必须初始化我们又知道了初始化列表才是成员变量真正定义的地方所以const 修饰的变量和引用就必须在初始化列表中初始化这样的代码就没有什么问题啦
class A
{
public:A(int x 0, int y 0):_x(x),_y(y){}
private:const int _x;int _y;
};
1.3 初始化列表的注意事项
我们来看看初始化列表的注意事项 1每个成员变量在初始化列表中最多出现一次。 2类中包含以下成员必须放在初始化列表位置进行初始化 引用成员变量 const成员变量 自定义类型成员且该类没有默认构造函数时。 我们来看第一点每个成员变量在初始化列表中最多出现一次。我们现在知道了初始化列表是成员变量定义的地方。如果一个成员变量在初始化列表中出现两次那么这个变量不就是被定义了两次嘛这当然是不被允许的 第二点 引用变量 与 const 修饰的成员变量为什么只能在初始化列表中初始化你应该已经明白了那么没有默认构造函数的自定义类型为什么也必须要在初始化列表中初始化呢
你还记得默认构造函数的定义嘛默认构造函数就是不用传参就可以直接调用的构造函数。
来看下面的代码我们定义了类A自己写了带参的构造函数那么编译器则不会为类A提供默认构造函数。我们又定义了类B成员变量是自定义类型。我们尝试在B的构造函数中给成员变量赋值。
class A
{
public:A(int x, int y):_x(x),_y(y){}
private:int _x;int _y;
};class B
{
public:B(A a){_a a;}private:A _a;
};
我们编译器直接报错了类 A 不存在默认构造函数为什么会报这个错误呢我们知道初始化列表是成员变量定义的地方那么类B的成员变量会在初始化列表中有类似这样的定义A _a。即在定义的过程中他会去调用A的默认构造函数但是因为 A 没有默认构造函数自然就会报错了 也就是说对于自定义类型的成员变量如果该成员变量没有显示在初始化列表中初始化 就会调用该自定义类型的默认构造函数。
那如果我们在初始化列表中显示初始化就会调用对应的构造函数对自定义类型的成员变量进行初始化。 1.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();return 0;
} A输出 1 1 B程序崩溃 C编译不通过 D输出 1 随机值 答案是D哦初始化列表的初始化顺序并不是按照初始化列表中的书写顺序来的而是按照成员变量的声明顺序来的_a2先声明那么就会先初始化_a2因此_a2用还未初始化的_a1初始化就是随机值。所以说为了避免出现这样的错误初始化列表的书写顺序最好与成员变量的声明顺序一致。 最后一点还记得我们在类中给成员变量缺省值的操作嘛那个缺省值其实就是给初始化列表初始化的
2. 同一表达式连续构造的优化
大家觉得下面的代码有没有问题呢
class A
{
public:A(int x):_x(x){}A(const A a){_x a._x;}private:int _x;
};int main()
{A a 2;return 0;
} 你变异之后发现变异通过并且运行良好这是为啥呢整形可以直接赋值给自定义类型其实不是的啦这其中发生了隐式类型转化C98的语法单参数的构造函数支持隐士类型转换。所以说 A a 2其实是用 2 这个整形调用了A的构造函数构造了一个A的对象然后在通过拷贝构造来构造 a 这个对象
如果你不能理解我们可以拿一个内置类型来举例
我们有一个整形变量直接将他赋值给一个double变量看看编译器是怎么做的。 这个道理和 A a 2是一样的撒所以在这一个表达式中调用了构造函数和拷贝构造函数于是编译器看不下去了他将这个表达式优化成了直接调用构造函数来构造 a 这个对象。编译器是容忍不了一个表达式出现两个构造函数的调用的
那你可能不相信我的解释你说有没有可能并没有一个临时对象被构造出来而本来就是用 2 直接构造a这个对象。那我们来看看这个代码A a 2; 报错非常量引用的初始值必须为左值也就是说在给 a 赋值的时候a引用的是一个右值自然就报错了。 可是我们如果用的是const的引用呢发现就没有报错了我们知道临时对象具有常性加上const才能对其进行引用。但是这也并不能说明通过2构造了一个A的对象哇你可能会说a就是引用的2这个右值嘛 OK那很巧C中有一个关键字能够废除这种隐式类型转换explicit。如果真如你所有说 a 能够引用 2 这个右值那么废除 这种隐式类型转换也不会报错吧 很遗憾他报错了那么说明这种隐式类型转换是真实存在的
3. 静态成员变量与静态成员函数
假设我们现在有这样一个需求需要你统计出一个类实例化出了多少个对象你激动的说到这个我会呀对象实例化不是都要调用构造函数吗对象销毁不是都要调用析构函数嘛那么我只要维护一个全局变量count然后再构造函数里对count析构函数里面对count--不就大功告成了吗是的你很厉害也很聪明
int _count;class A
{
public:A(int x 0):_x(x){_count;}A(const A a){_x a._x;_count;}~A(){_count--;}private:int _x;
};
可是有一天来了一个程序员看到了这个count这是干什么用的一下就给你把count给改了显然不行
原因还是在与全局变量无法得到有效的封转保护于是我们的静态成员变量他来了静态成员变量可以理解为全局变量的封转使得变量更好被维护。静态成员变量应该怎么初始化呢很简单 class A
{
public:A(int x 0):_x(x){_count;}A(const A a){_x a._x;_count;}~A(){_count--;}private:int _x;static int _count;
};
静态成员变量属于一个类不单独属于任何一个对象静态成员变量是所有对象共享的。访问静态变量的时候只要突破类域和访问权限就可以直接访问。可以通过类域也可以通过对象访问。 但是吧静态成员变量设置为共有又有一点不太好因此我们需要提供一个函数来访问静态成员变量。 我们提供了一个普通的成员函数发现必须需要有对象才能访问这个函数但是我们的需求是随时访问这个函数因此我们将这个函数变为静态函数静态函数没有this指针不能访问非静态的成员变量(因为函数访问普通的成员变量就是通过this指针访问的嘛)。通过对象或者类名都可以直接访问。前提是有权限哦 4. 内部类 内部类 概念如果一个类定义在另一个类的内部这个内部类就叫做内部类。 内部类是一个独立的类它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 注意内部类就是外部类的友元类参见友元类的定义内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。 特性 1. 内部类可以定义在外部类的public、protected、private都是可以的。 2. 注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。 3. sizeof(外部类) 外部类和内部类没有任何关系。 内部类C用的不多Java喜欢用内部类。内部类供大家了解一下吧