C++ 修饰符类型
C++ 允许在 char、int 和 double 数据类型前放置修饰符。
修饰符是用于改变变量类型的行为的关键字,它更能满足各种情境的需求。
下面列出了数据类型修饰符:
-
signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型。
-
unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍。
-
short:表示变量的范围比 int 更小。short int 可以缩写为 short。
-
long:表示变量的范围比 int 更大。long int 可以缩写为 long。
-
long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符。
-
float:表示单精度浮点数。
-
double:表示双精度浮点数。
-
bool:表示布尔类型,只有 true 和 false 两个值。
-
char:表示字符类型。
-
wchar_t:表示宽字符类型,可以存储 Unicode 字符。
修饰符 signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。
这些修饰符也可以组合使用,修饰符 signed 和 unsigned 也可以作为 long 或 short 修饰符的前缀。例如:unsigned long int。
C++ 允许使用速记符号来声明无符号短整数或无符号长整数。您可以不写 int,只写单词 unsigned、short 或 long,int 是隐含的。例如,下面的两个语句都声明了无符号整型变量。
signed int num1 = -10; // 定义有符号整型变量 num1,初始值为 -10 unsigned int num2 = 20; // 定义无符号整型变量 num2,初始值为 20 short int num1 = 10; // 定义短整型变量 num1,初始值为 10 long int num2 = 100000; // 定义长整型变量 num2,初始值为 100000 long long int num1 = 10000000000; // 定义长长整型变量 num1,初始值为 10000000000 float num1 = 3.14f; // 定义单精度浮点数变量 num1,初始值为 3.14 double num2 = 2.71828; // 定义双精度浮点数变量 num2,初始值为 2.71828 bool flag = true; // 定义布尔类型变量 flag,初始值为 true char ch1 = 'a'; // 定义字符类型变量 ch1,初始值为 'a' wchar_t ch2 = L'你'; // 定义宽字符类型变量 ch2,初始值为 '你'
为了理解 C++ 解释有符号整数和无符号整数修饰符之间的差别,我们来运行一下下面这个短程序:
实例
#include <iostream> using namespace std; /* * 这个程序演示了有符号整数和无符号整数之间的差别 */ int main() { short int i; // 有符号短整数 short unsigned int j; // 无符号短整数 j = 50000; i = j; cout << i << " " << j; return 0; }
当上面的程序运行时,会输出下列结果:
-15536 50000
上述结果中,无符号短整数 50,000 的位模式被解释为有符号短整数 -15,536。
C++ 中的类型限定符
类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。
限定符 | 含义 |
---|---|
const | const 定义常量,表示该变量的值不能被修改。。 |
volatile | 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
mutable | 表示类中的成员变量可以在 const 成员函数中被修改。 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。 |
const 实例
const int NUM = 10; // 定义常量 NUM,其值不可修改 const int* ptr = &NUM; // 定义指向常量的指针,指针所指的值不可修改 int const* ptr2 = &NUM; // 和上面一行等价
volatile 实例
volatile int num = 20; // 定义变量 num,其值可能会在未知的时间被改变
mutable 实例
class Example { public: int get_value() const { return value_; // const 关键字表示该成员函数不会修改对象中的数据成员 } void set_value(int value) const { value_ = value; // mutable 关键字允许在 const 成员函数中修改成员变量 } private: mutable int value_; };
static 实例
void example_function() { static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在 count++; }
register 实例
void example_function(register int num) { // register 关键字建议编译器将变量 num 存储在寄存器中 // 以提高程序执行速度 // 但是实际上是否会存储在寄存器中由编译器决定
笔记
C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。
C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个构造器 ,2 是个默认且隐含的类型转换操作符。
所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下(见下面权威的例子), 却违背了我们(程序员)的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
explicit构造函数的作用
解析:
explicit构造函数是用来防止隐式转换的。请看下面的代码:
class Test1 { public: Test1(int n) { num=n; }//普通构造函数 private: int num; }; class Test2 { public: explicit Test2(int n) { num=n; }//explicit(显式)构造函数 private: int num; }; int main() { Test1 t1=12;//隐式调用其构造函数,成功 Test2 t2=12;//编译错误,不能隐式调用其构造函数 Test2 t2(12);//显式调用成功 return 0; }
Test1的构造函数带一个int型的参数,代码23行会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit(显式),这表示不能通过隐式转换来调用这个构造函数,因此代码24行会出现编译错误。
普通构造函数能够被隐式调用。而explicit构造函数只能被显式调用。
volatile 往往会用于多线程的修饰,比如:
volatile boolean isNext = false; Thread A() { // 第一个工作 // isNext = true; } Thread B (){ if (isNext) { // 第二个工作 } }
这里volatile 就是从来标记isNext, 以确保线程B每次都重新从内存中读取isNext的值,第二个工作一定在第一个工作之后进行。
但是要注意,这里无法保证顺序性,应该编译器编译的时候会重新打乱两个语句的先后顺序,因此做第一个工作和赋值给isNext不一定会按照你代码顺序正常执行
16 位整数(短整数)的情况下,十进制 50000 就是二进制 11000011 01010000 但在有符号的情况下,二进制最左边的 1,代表这整个数字是负数但是电脑是以补码形式来表示数字的,要获得原本的数字,首先要把整个二进制数 - 11100001101010000 - 1 = 1100001101001111 然后,在把答案取反码 not 1100001101001111 = 0011110010110000 把最终答案变成十进制,就是 15536 所以,一开始的二进制数 11000011 01010000,在有符号的情况下代表的就是 -15536。
来自网友牢记圣光的笔记:
//上边的例子的二进制数为下边的运行结果,看到二进制就很好理解了! #include <iostream> #include <cstdlib> using namespace std; int main() { short int i; // 有符号短整数 short unsigned int j; // 无符号短整数 j = 50000; i = j; cout <<"i:" <<i<<'\n'<< "j:" << j<<endl; char s[40]; _itoa_s(i, s, 2); printf("变量i的二进制数为:%s\n", s); _itoa_s(j, s, 2); printf("变量j的二进制数为:%s\n", s); return 0; }
结果为:
i:-15536 j:50000 变量i的二进制数为:11111111111111111100001101010000 变量j的二进制数为:1100001101010000
验证 const
#include<iostream> using namespace std; int main() { const float PI = 3.14; PI = 3; //尝试修改 PI 的值,将会出现错误提示。 float radius = 10.0; float area = PI * radius * radius; float circumference = 2 * PI * radius; cout << "area is "<<area <<", circumference is "<< circumference<<endl; return 0; }