跳到主要内容

C++ 修饰符

什么是C++修饰符?

在C++编程语言中,修饰符是用来改变或修饰变量、函数和类等基本类型的关键字。修饰符可以影响标识符的存储特性、作用域、生命周期和访问权限等。掌握这些修饰符的正确使用对于编写高效、安全的代码至关重要。

C++中的修饰符主要分为三大类:

  1. 访问修饰符
  2. 类型修饰符
  3. 存储类修饰符

让我们逐一了解它们的用途和使用方法。

访问修饰符

访问修饰符用于确定类的成员(变量和函数)的可访问性。C++提供了三种访问修饰符:

1. public

public修饰符表示成员可以被任何函数访问,包括类外部的函数。

2. private

private修饰符限制成员只能被同一类中的函数访问,类外部的函数无法直接访问这些成员。

3. protected

protected修饰符类似于private,但允许派生类(子类)访问基类(父类)的成员。

cpp
class MyClass {
public:
int publicVar; // 可以被任何地方访问
void publicMethod() {
cout << "Public method called" << endl;
}

private:
int privateVar; // 只能在类内部访问
void privateMethod() {
cout << "Private method called" << endl;
}

protected:
int protectedVar; // 只能在类内部和派生类中访问
void protectedMethod() {
cout << "Protected method called" << endl;
}
};

int main() {
MyClass obj;
obj.publicVar = 10; // 正确,公有成员
obj.publicMethod(); // 正确,公有成员

// obj.privateVar = 20; // 错误,无法访问私有成员
// obj.privateMethod(); // 错误,无法访问私有成员

// obj.protectedVar = 30; // 错误,无法从类外部访问保护成员
// obj.protectedMethod(); // 错误,无法从类外部访问保护成员

return 0;
}
备注

如果没有指定访问修饰符,类的成员默认为private,而结构体的成员默认为public

类型修饰符

类型修饰符用于改变数据类型的特性。常见的类型修饰符包括:

1. const

const关键字表示变量的值不能被修改。

cpp
const int MAX_VALUE = 100;
// MAX_VALUE = 200; // 错误,不能修改const变量

2. volatile

volatile关键字告诉编译器,变量的值可能会在程序的控制或检测之外被改变(如硬件直接修改内存)。

cpp
volatile int sensor_value; // 可能被硬件修改的变量

3. signed 和 unsigned

signed允许变量存储正数和负数,而unsigned只允许存储非负数。

cpp
signed int num1 = -10;    // 可以存储负数
unsigned int num2 = 10; // 只能存储非负数
// unsigned int num3 = -5; // 错误的用法,会导致意外结果

4. short 和 long

这些修饰符改变整数类型的大小。

cpp
short int smallNumber = 32767;        // 通常为2字节
long int bigNumber = 2147483647; // 通常为4字节
long long int veryBigNumber = 9223372036854775807LL; // 通常为8字节

存储类修饰符

存储类修饰符定义了变量和函数的范围(可见性)和生命周期。

1. auto

在C++11之前,auto是默认的存储类,表示局部变量。在C++11及以后,auto用于类型推导。

cpp
auto i = 10;      // 编译器自动推导i为int类型
auto d = 10.5; // 编译器自动推导d为double类型
auto s = "Hello"; // 编译器自动推导s为const char*类型

2. register

register修饰符建议编译器将变量存储在CPU寄存器中,以加快访问速度。

cpp
register int counter = 0; // 建议存储在寄存器中
警告

在现代C++中,register关键字基本被弃用,因为编译器的优化能力已经足够强大。

3. static

static修饰符有两种用途:

  • 当应用于局部变量时,它使变量在函数调用间保持其值。
  • 当应用于全局变量或函数时,它将其可见性限制在当前文件内。
cpp
void incrementCounter() {
static int count = 0; // 只初始化一次
count++;
cout << "Counter: " << count << endl;
}

int main() {
incrementCounter(); // 输出: Counter: 1
incrementCounter(); // 输出: Counter: 2
incrementCounter(); // 输出: Counter: 3
return 0;
}

4. extern

extern声明一个变量或函数在其他文件中定义,使当前文件可以使用它。

cpp
// 在一个文件中
extern int globalVar; // 声明变量在其他地方定义
extern void someFunction(); // 声明函数在其他地方定义

// 在另一个文件中
int globalVar = 100; // 实际定义
void someFunction() {
// 函数实现
}

5. mutable

mutable允许const成员函数修改类中被声明为mutable的成员变量。

cpp
class Example {
private:
mutable int accessCount;
int value;

public:
Example(int v) : value(v), accessCount(0) {}

int getValue() const {
accessCount++; // 即使在const函数中也可以修改
return value;
}

int getAccessCount() const {
return accessCount;
}
};

int main() {
const Example ex(10);
cout << "Value: " << ex.getValue() << endl; // 输出: Value: 10
cout << "Access count: " << ex.getAccessCount() << endl; // 输出: Access count: 1
return 0;
}

实际应用案例

案例1:常量与引用结合

在实际开发中,我们经常需要传递大型对象作为函数参数。为了提高效率,我们可以使用常量引用:

cpp
// 传值方式(低效,会复制整个对象)
void processData(vector<int> data) {
// 处理数据
}

// 常量引用方式(高效,不会复制对象,且保证不会修改原数据)
void processData(const vector<int>& data) {
// 处理数据,但不能修改data
}

int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
processData(numbers); // 使用常量引用,高效且安全
return 0;
}

案例2:静态成员实现计数器

静态成员可以用来跟踪类的实例数量:

cpp
class Counter {
private:
static int count; // 所有实例共享的静态成员
int id;

public:
Counter() {
id = ++count;
cout << "Created Counter object #" << id << endl;
}

~Counter() {
cout << "Destroyed Counter object #" << id << endl;
count--;
}

static int getCount() {
return count;
}
};

int Counter::count = 0; // 静态成员需要在类外初始化

int main() {
cout << "Initial count: " << Counter::getCount() << endl; // 输出: Initial count: 0

Counter c1; // 输出: Created Counter object #1
Counter c2; // 输出: Created Counter object #2

{
Counter c3; // 输出: Created Counter object #3
cout << "Current count: " << Counter::getCount() << endl; // 输出: Current count: 3
} // 输出: Destroyed Counter object #3

cout << "Final count: " << Counter::getCount() << endl; // 输出: Final count: 2

return 0;
} // c1和c2在这里被销毁

案例3:访问修饰符在继承中的应用

cpp
class Shape {
protected:
int width;
int height;

public:
Shape(int w, int h) : width(w), height(h) {}
virtual int area() const = 0; // 纯虚函数
};

class Rectangle : public Shape {
public:
Rectangle(int w, int h) : Shape(w, h) {}

int area() const override {
return width * height; // 可以访问基类的protected成员
}
};

class Triangle : public Shape {
public:
Triangle(int w, int h) : Shape(w, h) {}

int area() const override {
return (width * height) / 2; // 可以访问基类的protected成员
}
};

int main() {
Rectangle rect(5, 4);
Triangle tri(5, 4);

cout << "Rectangle area: " << rect.area() << endl; // 输出: Rectangle area: 20
cout << "Triangle area: " << tri.area() << endl; // 输出: Triangle area: 10

return 0;
}

总结

C++修饰符是编程语言的重要组成部分,正确使用修饰符可以:

  1. 控制类成员的访问权限(public、private、protected)
  2. 修改数据类型的特性(const、volatile、signed、unsigned等)
  3. 控制变量的存储方式和生命周期(static、extern、register等)

掌握这些修饰符的用法对于编写高效、安全和可维护的C++代码至关重要。在实际开发中,合理选择修饰符可以提高代码质量,减少错误,并增强性能。

练习

  1. 创建一个类,包含公共、私有和保护成员,然后创建一个子类,测试不同成员的可访问性。
  2. 编写一个函数,使用静态局部变量来计算该函数被调用的次数。
  3. 创建一个包含const成员和mutable成员的类,并演示如何在const成员函数中修改mutable成员。
  4. 实现一个单例模式,使用static和private访问修饰符确保只能创建一个类实例。
提示

编程练习是巩固所学知识的好方法。尝试实现上述练习题目,这将帮助你更好地理解和应用C++修饰符。

扩展资源