网站首页 > 基础教程 正文
支持位段类型的类成员的直接初始化
C++ 20 增加了支持位段类型的类成员的初始化,例如下面的代码:
#include <iostream>
class CA
{
public:
int m_a1 { 2 }; // (1)C++11开始支持,大括号方式的普通变量初始化
int m_a2 = 4; // (2)C++11开始支持,等号方式的普通变量初始化
int m_b1 : 5 { 6 }; // (3)C++20开始支持,大括号方式的位段变量初始化
int m_b2 : 11 = 8; // (4)C++20开始支持,等号方式的位段变量初始化
};
int main( int argc, char * argv[] )
{
CA a1;
std::cout << a1.m_a1 << " " << a1.m_a2 << std::endl;
std::cout << a1.m_b1 << " " << a1.m_b2 << std::endl;
return 0;
}
使用GCC编译时,指定不同的C++标准时,编译的结果也不一样:
[foo@test code]$ g++ -std=c++98 a2.cpp
a2.cpp:6:15: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’
6 | int m_a1 { 2 };
| ^
a2.cpp:7:13: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’
7 | int m_a2 = 4;
| ^
a2.cpp:8:13: error: expected ‘;’ at end of member declaration
8 | int m_b1 : 5 { 6 };
| ^
| ;
a2.cpp:8:15: error: expected unqualified-id before ‘{’ token
8 | int m_b1 : 5 { 6 };
| ^
a2.cpp:9:18: error: an assignment cannot appear in a constant-expression
9 | int m_b2 : 11 = 8;
| ^
a2.cpp:6:11: warning: extended initializer lists only available with ‘-std=c++11’ or ‘-std=gnu++11’
6 | int m_a1 { 2 };
| ^
a2.cpp:6:15: warning: extended initializer lists only available with ‘-std=c++11’ or ‘-std=gnu++11’
6 | int m_a1 { 2 };
| ^
[foo@test code]$ g++ -std=c++11 a2.cpp
a2.cpp:8:15: warning: default member initializers for bit-fields only available with ‘-std=c++2a’ or ‘-std=gnu++2a’
8 | int m_b1 : 5 { 6 };
| ^
a2.cpp:9:16: warning: default member initializers for bit-fields only available with ‘-std=c++2a’ or ‘-std=gnu++2a’
9 | int m_b2 : 11 = 8;
| ^
[foo@test code]$ ./a.out
2 4
6 8
[foo@test code]$ g++ -std=c++20 a2.cpp
[foo@test code]$ ./a.out
2 4
6 8
类成员变量初始化的延伸分析
在定义类的局部变量或者使用 new 动态创建类实例时,会调用类的构造函数,如果没有显式定义类的构造函数,会使用类的默认构造函数。在类的默认构造函数中,对于简单类型的成员变量,默认是不会初始化的,也就是它的值是不确定的,这种不确定性是潜在问题的根源。因此对这种简单类型的类成员进行显式初始化是一个良好的编程习惯。
早期的 C++ 需要单独编写构造函数来进行类成员的初始化,例如:
class CA2
{
public:
CA2() : m_c1( 2 ), m_c2( 4 ) { } // (1)使用构造函数进行初始化
int m_c1;
int m_c2;
};
这样的写法虽然直观,但有点冗长,因此在 C++ 11 标准里面,模仿局部变量初始化的方式,增加了类成员变量的直接初始化,支持使用大括号和等号的方式初始化:
class CA3
{
public:
int m_c1 { 3 };
int m_c2 = 6;
};
其实局部变量也可以使用圆括号进行初始化,但是圆括号进行初始化时,如果使用另一个变量进行初始化,变量又跟类型重名 ,就和定义一个函数雷同,产生二义性。因此局部变量不推荐使用圆括号来初始化,C++11 扩展支持类成员初始化时,就干脆不支持圆括号进行初始化了。
在这样的直接初始化基础上,还可以再定义CA3类的构造函数,如果构造函数中有成员变量的初始化,则以构造函数为准,如果未显式初始化,则沿用定义成员变量时的初始化,这样不同的构造函数可以有选择的只初始化部分成员变量,不需要每个构造函数都重复将所有成员变量都初始化。
C++ 11 中只支持普通类型的直接初始化,不支持位段的直接初始化,直到C++ 20 才支持
主要是因为定义位段的大小,和后面的等号初始化,容易产生二义性,所以需要较多的讨论,最后明确要求等号用于初始化,而不会跟前面的位段大小组成表达式。例如:
static int g1 = 3;
static const int g2 = 4;
class CA4
{
public:
int m_c1 : (g1 = 6); // (1)Error,赋值本身语法正确,但不是常量,而定义位段大小必须使用常量
int m_c2 : (g2 = 6); // (2)Error,常量不能赋值
int m_c3 : g2 = 6; // (3)OK,位段大小g2是常量,最后的 = 6 表示初始化,不会当(2)来解析
int m_c4 : true ? g2 : (g1 = 3); // (4)OK,g1赋值语法上正确,虽然不是常量,但实际并不会用到,也不会执行
int m_c5 : true ? g2 : g1 = 3; // (5)OK,最后的 = 3 表示初始化,不会当(4)来解析
};
从上面例子可以看到,位段大小如果使用 “ ? : ” 运算符,和等号初始化,会产生二义性,现在标准强制要求必须按初始化来解析。不过实际位段一般都是直接的数值常量,不会使用很复杂的表达式的。
猜你喜欢
- 2024-10-10 C++系列1-1:初探C++ c=2μf
- 2024-10-10 浅谈C++11(第9篇 可变参数模板) c++可变参数模板类
- 2024-10-10 数组的初始化方式有哪几种? 数组的初始化是什么意思
- 2024-10-10 c++对于内建类型的默认初始化 创建内部类的对象
- 2024-10-10 C++类构造函数,如何初始化对象?linux C++第27讲
- 2024-10-10 【C++编程语言】之 类和对象——对象的初始和消除
- 2024-10-10 C++基础语法梳理:引用、封装、继承和多态
- 2024-10-10 C++核心准则?:按照成员声明的顺序定义和初始化成员变量
- 2024-10-10 C++核心准则C.47:按照成员变量声明的次序定义和初始化数据成员
- 2024-10-10 C/C++语言编程系列002——不同情况下数组的初始化方法
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)