文章目录
一、C++ 修饰符类型
在C++中,修饰符(Modifiers)用于改变变量、函数、类成员等的默认行为或属性。这些修饰符可以分为几个主要的类别,包括访问修饰符(Access Modifiers)、存储类修饰符(Storage Class Specifiers)、类型修饰符(Type Modifiers)以及函数修饰符(如const
和noexcept
用于函数)。下面是对这些修饰符类型的一些简要说明:
1. 访问修饰符(Access Modifiers)
访问修饰符控制类成员的访问权限。
public
:类外部可以访问。protected
:只能被其派生类以及该类的成员访问。private
:只能被该类内部的成员函数、友元函数以及类本身访问。
2. 存储类修饰符(Storage Class Specifiers)
存储类修饰符决定了变量/函数的存储方式和作用域。
auto
:自动变量(局部变量),但现代C++中auto
关键字有了新的用途,即自动类型推导。register
:建议编译器尽可能将变量存储在寄存器中(但现在很多编译器都忽略了这个建议)。static
:- 对于局部变量,
static
延长了变量的生命周期,但变量的作用域不变。 - 对于全局变量和函数,
static
限制了它们的链接性(linkage),使得它们只在定义它们的文件内可见。 - 对于类成员变量,
static
使得变量成为所有对象的共享变量。
- 对于局部变量,
extern
:声明变量或函数是在别的文件中定义的,或者是全局的。thread_local
(C++11):声明变量的生命周期与线程相同,每个线程都拥有该变量的一个独立实例。
3. 类型修饰符(Type Modifiers)
类型修饰符影响变量或函数的类型或特性。
const
:表示变量或函数的返回值不可修改。volatile
:告诉编译器该变量的值可能会在程序的控制之外被改变(例如,被操作系统、硬件或其他线程改变)。signed
和unsigned
:用于整数类型,指定变量是有符号还是无符号的。long
和short
:用于整数类型,指定变量的存储大小。enum
:定义枚举类型。struct
和class
:定义复合数据类型。
4. 函数修饰符
const
:用于成员函数,表示该函数不会修改对象的任何成员变量(如果是非静态成员函数)。noexcept
(C++11):指定函数不会抛出任何异常。如果函数确实抛出了异常,程序会调用std::terminate()
立即终止。virtual
:允许在派生类中重写基类的成员函数。override
(C++11):明确指定一个成员函数是重写了基类中的虚函数。final
(C++11):用于类和方法,防止类被继承或方法被进一步重写。
二、C++ 修饰符类型-案例
当然,下面我将提供几个使用不同C++修饰符的案例代码。这些案例将涵盖访问修饰符、存储类修饰符、类型修饰符以及函数修饰符。
1. 访问修饰符案例
class MyClass { public: void publicFunction() { // 公有成员函数,可以在类外部访问 } protected: void protectedFunction() { // 保护成员函数,只能在类内部、派生类以及友元中访问 } private: void privateFunction() { // 私有成员函数,只能在类内部访问 } };
2. 存储类修饰符案例
#include <iostream> class MyClass { public: static int staticVar; // 静态成员变量,所有对象共享 void printStaticVar() { std::cout << staticVar << std::endl; } }; int MyClass::staticVar = 0; // 静态成员变量的定义 int main() { MyClass obj1, obj2; obj1.staticVar = 10; // 通过任一对象修改 obj2.printStaticVar(); // 输出 10 extern int globalVar; // 声明外部变量 // 假设 globalVar 在其他地方定义并初始化 // register 修饰符在现代C++中通常不推荐使用,因为它对编译器来说是建议性的 // register int regVar; // 示例,但通常不推荐这样做 return 0; } // 假设在另一个文件中定义的 globalVar // int globalVar = 20;
3. 类型修饰符案例
#include <iostream> class MyClass { public: void display() const { // const 成员函数,保证不修改任何成员变量 std::cout << "Display function called" << std::endl; } // 使用 signed 和 unsigned void showIntegers() { signed int sInt = -10; unsigned int uInt = 10; std::cout << "Signed: " << sInt << ", Unsigned: " << uInt << std::endl; } }; int main() { MyClass obj; obj.display(); obj.showIntegers(); return 0; }
4. 函数修饰符案例
#include <iostream> #include <stdexcept> class MyClass { public: virtual void virtualFunction() { // 虚函数,可以在派生类中被重写 std::cout << "Base class function" << std::endl; } void myFunction() noexcept { // noexcept 函数,保证不抛出异常 // 如果尝试抛出异常,程序将调用 std::terminate() // 注意:这里的函数体没有实际抛出异常 } void overriddenFunction() override { // 使用 override 明确指定重写 // 这里是重写基类中的虚函数(假设基类中有相应的虚函数声明) std::cout << "Overridden function" << std::endl; } // 注意:final 修饰符用于类和方法时,表示该类不能被继承或该方法不能在派生类中被重写 // 这里没有直接展示 final 的用法,因为它需要在类的继承关系中体现 }; // 假设有 MyClass 的派生类,可以展示 virtual 和 override 的用法 // class DerivedClass : public MyClass { // public: // void virtualFunction() override { // // 重写 MyClass 中的 virtualFunction // std::cout << "Derived class function" << std::endl; // } // }; int main() { MyClass obj; obj.virtualFunction(); // 调用基类函数 obj.myFunction(); // 调用 noexcept 函数 // obj.overriddenFunction(); // 如果 MyClass 中有对应的虚函数声明,则可以这样调用 // 演示 noexcept 的异常处理(尽管这个例子没有实际抛出异常) // try { // throw std::runtime_error("Error"); // obj.myFunction(); // 假设 myFunction 内部有抛出异常的代码,但由于 noexcept,这里不会执行 // } catch (...) { // std::cout << "Exception caught, but myFunction() is noexcept and should not have thrown" << std::endl; // // 注意:如果 myFunction 实际上是 noexcept 的,并且抛出了异常,程序将不会进入这个 catch 块 // // 相反,它将调用 std::terminate() // } }
三、C++ 中的类型限定符
在C++中,类型限定符(Type Qualifiers)主要用于限制或改变变量的类型或行为。最常见的类型限定符包括const
和volatile
。这些限定符可以与基本数据类型、指针、引用等一起使用,以提供额外的信息给编译器和开发者。
1. const
限定符
const
限定符用于声明一个变量为常量,意味着一旦该变量被初始化后,其值就不能被改变。const
也可以用于修饰函数参数、返回值以及成员函数,以表明这些函数不会(或不应该)修改任何数据。
案例代码:
#include <iostream> void display(const int& value) { // 尝试修改value会导致编译错误 // value = 10; // 编译错误 std::cout << "Value: " << value << std::endl; } int main() { const int a = 5; // a是一个常量,其值不能被修改 // a = 10; // 编译错误 display(a); // 传递a的引用,但由于是const,所以display函数内部不能修改它 const int* ptr = &a; // ptr是一个指向常量的指针,不能通过ptr修改a的值 // *ptr = 10; // 编译错误 // 但是,ptr本身是可以被修改的,指向另一个常量 const int b = 20; ptr = &b; // 合法,ptr现在指向b // 指向常量的指针的常量(即,指针本身也是常量) int c = 30; const int* const cPtr = &c; // cPtr不能指向其他地址,也不能通过cPtr修改c的值 // cPtr = &a; // 编译错误,cPtr是常量 return 0; }
2. volatile
限定符
volatile
限定符告诉编译器该变量的值可能会以编译器无法预见的方式被改变。这通常用于嵌入式编程、多线程编程或访问由硬件直接修改的内存位置时。编译器在优化代码时会考虑变量的volatile
属性,以确保每次访问该变量时都直接从其内存地址读取值,而不是使用可能已存储在寄存器中的缓存值。
案例代码:(这个案例可能不那么直观,因为它依赖于外部条件来改变变量的值)
#include <iostream> // 假设有一个硬件寄存器,其值可能会被外部事件(如中断)改变 volatile int hardwareRegister = 0; // 声明为volatile,因为值可能随时改变 void checkHardwareStatus() { // 循环检查hardwareRegister的值,直到它变为非零(表示某种硬件事件) while (hardwareRegister == 0) { // 在这里,编译器不会优化掉对hardwareRegister的访问 // 即使它看起来像是在一个无限循环中 } // 处理硬件事件... std::cout << "Hardware event detected!" << std::endl; } int main() { // 在实际应用中,hardwareRegister的值可能由中断服务例程(ISR)改变 // 这里我们模拟这个行为 // ...(模拟代码省略) checkHardwareStatus(); return 0; }
volatile
的使用应谨慎,因为它会阻止编译器进行某些优化,这可能会降低程序的性能。只有当确实需要时才应使用volatile
。