C++中几个比较不常用的关键字

本文介绍了C++中几个不常见关键字的用法。mutable用于在const成员函数中改变非静态数据成员;volatile可让编译器不持有变量临时拷贝,解决多线程中编译器优化的问题;explicit禁止单参数构造函数用于自动型别转换;__based用于解决共享内存问题,但会牺牲一定效率。


mutable关键字


关键字mutable是C++中一个不常用的关键字,他只能用于类的非静态和非常量数据成员
我们知道一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,
对像的状态也会随之发生变化!

如果一个类的成员函数被声明为const类型,表示该函数不会改变对象的状态,也就是
该函数不会修改类的非静态数据成员.但是有些时候需要在该类函数中对类的数据成员
进行赋值.这个时候就需要用到mutable关键字了

例如:
class Demo
{
public:
    Demo(){}
    ~Demo(){}
public:
    bool getFlag() const
    {
        m_nAccess++;
        return m_bFlag;
    }
private:
    int  m_nAccess;
    bool m_bFlag;
};

int main()
{
    return 0;
}

编译上面的代码会出现 error C2166: l-value specifies const object的错误
说明在const类型的函数中改变了类的非静态数据成员.

这个时候需要使用mutable来修饰一下要在const成员函数中改变的非静态数据成员
m_nAccess,代码如下:

class Demo
{
public:
    Demo(){}
    ~Demo(){}
public:
    bool getFlag() const
    {
        m_nAccess++;
        return m_bFlag;
    }
private:
    mutable int  m_nAccess;
    bool m_bFlag;
};

int main()
{
    return 0;
}

这样再重新编译的时候就不会出现错误了!

 

 

volatile关键字

volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型
如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者
类的所有成员都会被视为volatile.

使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要
例如:
int i;
i = i + 3;
无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。一般来说,volatitle
关键字适用于行与行之间,而不是放在行内。

我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正
这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法)

void getKey(char* pch)
{
 while (*pch == 0)
  ;
}

当你在VC开发环境中将最优化选项都关闭之后,编译这个程序,将获得以下结果(汇编代码)
;       while (*pch == 0)
$L27
 ; Load the address stored in pch
 mov eax, DWORD PTR _pch$[ebp]
 ; Load the character into the EAX register
 movsx eax, BYTE PTR [eax]
 ; Compare the value to zero
 test eax, eax
 ; If not zero, exit loop
 jne $L28
 ;
 jmp $L27
$L28
;}

这段没有优化的代码不断的载入适当的地址,载入地址中的内容,测试结果。效率相当的低,但是结果非常准确

现在我们再来看看将编译器的所有最优化选项开关都打开以后,重新编译程序,生成的汇编代码,和上面的代码
比较一下有什么不同
;{
 ; Load the address stored in pch
 mov eax, DWORD PTR _pch$[esp-4]
 ; Load the character into the AL register
 movsx al, BYTE PTR [eax]
; while (*pch == 0)
 ; Compare the value in the AL register to zero
 test al, al
 ; If still zero, try again
 je SHORT $L84
 ;
;}

从代码的长度就可以看出来,比没有优化的情况要短的多。需要注意的是编译器把MOV指令放到了循环之外。这在
单线程中是一个非常好的优化,但是,在多线程应用程序中,如果另一个线程改变了变量的值,则循环永远不会
结束。被测试的值永远被放在寄存器中,所以该段代码在多线程的情况下,存在一个巨大的BUG。解决方法是重新
写一次getKey函数,并把参数pch声明为volatile,代码如下:

void getKey(volatile char* pch)
{
 while (*pch == 0)
  ;
}

这次的修改对于非最优化的版本没有任何影响,下面请看最优化后的结果:

;{
 ; Load the address stored in pch
 mov eax, DWORD PTR _pch$[esp-4]
;       while (*pch == 0)
$L84:
 ; Directly compare the value to zero
 cmp BYTE PTR [eax], 0
 ; If still zero, try again
 je SHORT $L84
 ;
;}

这次的修改结果比较完美,地址不会改变,所以地址声明被移动到循环之外。地址内容是volatile,所以每次循环
之中它不断的被重新检查。

把一个const volatile变量作为参数传递给函数是合法的。如此的声明意味着函数不能改变变量的值,但是变量的
值却可以被另一个线程在任何时间改变掉。


explicit关键字


我们在编写应用程序的时候explicit关键字基本上是很少使用,它的作用是"禁止单参数构造函数"被用于自动型别转换,
其中比较典型的例子就是容器类型,在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数.
例如:
你可以声明这样一个构造函数
class Array
{
public:
 explicit Array(int size);
 ......
};
在这里explicit关键字起着至关重要的作用,如果没有这个关键字的话,这个构造函数有能力将int转换成Array.一旦这种
情况发生,你可以给Array支派一个整数值而不会引起任何的问题,比如:
Array arr;
...
arr = 40;
此时,C++的自动型别转换会把40转换成拥有40个元素的Array,并且指派给arr变量,这个结果根本就不是我们想要的结果.如果
我们将构造函数声明为explicit,上面的赋值操作就会导致编译器报错,使我们可以及时发现错误.
需要注意的是:explicit同样也能阻止"以赋值语法进行带有转型操作的初始化";
例如:
Array arr(40);//正确
Array arr = 40;//错误

看一下以下两种操作:
X x;
Y y(x);//显式类型转换
另一种
X x;
Y y = x;//隐式类型转换

这两种操作存在一个小小的差别,第一种方式式通过显式类型转换,根据型别x产生了型别Y的新对象;第二种方式通过隐式转换
产生了一个型别Y的新对象.
explicit关键字的应用主要就是上面所说的构造函数定义种,参考该关键字的应用可以看看STL源代码,其中大量使用了该关键字

 

__based关键字


该关键字主要用来解决一些和共享内存有关的问题,它允许指针被定义为从某一点开始算的32位偏移值,而不是内存种的绝对位置
举个例子:

typedef struct tagDEMOSTRUCT {
 int a;
 char sz[10];
} DEMOSTRUCT, * PDEMOSTRUCT;

HANDLE hFileMapping = CreateFileMapping(...);
LPVOID lpShare = (LPDWORD)MapViewOfFile(...);

DEMOSTRUCT __based(lpShare)* lpDemo;

上面的例子声明了一个指针lpDemo,内部储存的是从lpShare开始的偏移值,也就是lpHead是以lpShare为基准的偏移值.
上面的例子种的DEMOSTRUCT只是随便定义的一个结构,用来代表任意的结构.

虽然__based指针使用起来非常容易,但是,你必须在效率上付出一定的代价.每当你用__based指针处理数据,CPU都必须
为它加上基地址,才能指向真正的位置.

 

在这里我只是介绍了几个并不时很常见的关键字的意义即用法,其他那些常见的关键字介绍他们的文章已经不少了在这里
就不再一一介绍了.希望这些内容能对大家有一定的帮助!

<think>首先,用户的问题是关于 "C++常用关键字" 的。所以,我需要基于提供的引用信息来回答这个问题。 回顾系统指令: - 回答必须使用中文。 - 对于数学表达式,使用特定的LaTeX格式,但在这个主题中,可能涉及数学表达式,所以我可以忽略。 - 结构清晰,帮助用户逐步解决问题。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如 [^1]。 用户提供了四个引用: - 引用[1]: 列出了C/C++常见关键字,并提到了一些专栏文章。 - 引用[2]: 解释了static关键字C++中的用法,包括静态全局变量。 - 引用[3]: 讨论了static修饰成员函数,以及#define和typedef。 - 引用[4]: 讨论了const关键字,特别是const指针,还有goto语句。 我需要综合这些信息来回答用户的问题:"我想查找C++常用关键字的相关信息 请问C++常用关键字"。 结构化回答: 1. **引言**:简要介绍C++常用关键字。 2. **详细解释**:基于引用,解释一些常用关键字,如static, const等。 3. **生成相关问题**:在结尾添加相关问题。 从引用中提取关键点: - 常用关键字包括:static, const, goto, 以及#define和typedef(虽然关键字,但密切相关)。 - 引用[1]提到常见的关键字如auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while。但引用[1]没有详细列出,只是说"常见的C/C++关键字如下所示",但原文中被截断了。 - 重点放在提供的引用上:static, const, goto。 所以,我可以说C++常用关键字包括static, const, goto等,并详细解释它们。 在回答中引用: - 当使用某个引用时,在段落末尾自然添加 [^n]。 回答结构: - 开头:解释什么是C++关键字。 - 主体:列出并解释几个常用关键字。 - 基于引用[2],解释static关键字。 - 基于引用[4],解释const关键字。 - 基于引用[3],解释static成员函数。 - 基于引用[4],解释goto语句。 - 结尾:简要总结,并生成相关问题。 生成相关问题: - 类似之前的示例:"§§相关问题§§",然后列出几个问题。 确保回答真实可靠:只基于提供的引用信息。 最终回答大纲: 1. 介绍C++关键字的重要性。 2. 详细解释static关键字:静态全局变量、静态成员函数(引用[2]和[3])。 3. 解释const关键字:常量指针、指针常量等(引用[4])。 4. 解释其他关键字如goto(引用[4])。 5. 总结。 6. 相关问题。 </think>以下是对C++常用关键字的详细解释。C++关键字是语言中预定义的保留字,用于声明变量、控制流程、定义类型等。它们构成程序的基础结构,正确理解这些关键字对编程至关重要。我会基于您提供的引用信息,逐一解释最常用的一些关键字(如 `static`、`const`、`goto` 等),并结合实际代码示例说明其用法。需要注意的是,C++标准定义了约90个关键字(如 `auto`, `break`, `class`, `const`, `static`, `goto` 等),但此处重点聚焦在引用中强调的常用场景。 ### 1. `static` 关键字 `static` 是C++中最常用关键字之一,它控制变量的存储方式和可见性,可分为两类用法: - **面向过程的用法**:用于全局变量或局部变量。 - **静态全局变量**:在全局变量前加 `static`,限定其作用域仅在当前文件内,避免命名冲突。例如: ```cpp #include <iostream> using namespace std; static int n; // 静态全局变量,仅在当前文件可见 void fun(); int main() { n = 20; cout << n << endl; // 输出: 20 fun(); // 输出: 21 return 0; } void fun() { n++; // 修改静态全局变量 cout << n << endl; } ``` 此例中,`n` 的生命周期延长至整个程序运行期间,但只能被本文件访问[^2]。 - **面向对象的用法**:用于类成员。 - **静态成员函数**:与特定对象关联,可直接通过类名调用。这常用于工具函数或共享数据操作。 ```cpp class MyClass { public: static void staticMethod() { // 静态成员函数 cout << "Static method called" << endl; } }; int main() { MyClass::staticMethod(); // 推荐调用方式:类名直接调用 return 0; } ``` 静态成员函数依赖于对象实例,适合处理类级别逻辑[^3]。 `static` 的优点包括减少内存占用(共享存储)和封装作用域,但需注意线程安全问题。 ### 2. `const` 关键字 `const` 用于声明常量,确保变量值被修改,尤其在指针操作中很常见: - **修饰变量**:使变量成为只读常量。 ```cpp const int MAX = 100; // 常量,值可更改 ``` - **修饰指针**:分为三种情况(基于指针指向和值的可变性): - **常量指针**:指针指向可改,但指向的值可改(`const int*`)。 ```cpp int a = 10, b = 20; const int* p = &a; // 常量指针 // *p = 30; // 错误:值可修改 p = &b; // 正确:指向可修改 ``` - **指针常量**:指针指向可改,但指向的值可改(`int* const`)。 ```cpp int a = 10; int* const p = &a; // 指针常量 *p = 20; // 正确:值可修改 // p = &b; // 错误:指向可修改 ``` - **常量指针常量**:指向和值均可改(`const int* const`)。 ```cpp const int a = 10; const int* const p = &a; // 指向和值都可改 ``` `const` 提升代码健壮性,防止意外修改数据,常在函数参数和类成员中使用[^4]。 ### 3. `goto` 关键字 `goto` 用于无条件跳转到标签位置,但现代编程中推荐使用,易导致代码难以维护: ```cpp #include <iostream> using namespace std; int main() { int i = 0; loop: // 标签定义 cout << i << " "; i++; if (i < 5) goto loop; // 跳转到标签 return 0; } // 输出: 0 1 2 3 4 ``` 尽管 `goto` 在简单循环中可用,但易引发“面条代码”,建议优先使用 `for` 或 `while` 替代[^4]。 ### 其他常用关键字简要说明 - **`auto`**:自动类型推导,简化变量声明(如 `auto x = 10;`)。 - **`class`**:定义类结构,封装数据和函数。 - **`break`/`continue`**:控制循环流程(`break` 退出循环,`continue` 跳到下一次迭代)。 - **`typedef` 和 `#define`**:虽然关键字,但常用于类型重命名(`typedef int MyInt;`)和宏定义(`#define PI 3.14`),前者更安全[^3]。 ### 总结 C++常用关键字包括 `static`(控制作用域和存储)、`const`(确保常量性)、`goto`(跳转控制)等,它们是构建高效可靠程序的基础。掌握这些关键字能避免常见错误,如数据竞争或意外修改。实践中,应优先使用现代C++特性(如智能指针替代裸指针)以提高安全性。更多案例可参考相关专栏文章[^1][^2][^3][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值