C++ 修饰符
什么是C++修饰符?
在C++编程语言中,修饰符是用来改变或修饰变量、函数和类等基本类型的关键字。修饰符可以影响标识符的存储特性、作用域、生命周期和访问权限等。掌握这些修饰符的正确使用对于编写高效、安全的代码至关重要。
C++中的修饰符主要分为三大类:
- 访问修饰符
- 类型修饰符
- 存储类修饰符
让我们逐一了解它们的用途和使用方法。
访问修饰符
访问修饰符用于确定类的成员(变量和函数)的可访问性。C++提供了三种访问修饰符:
1. public
public
修饰符表示成员可以被任何函数访问,包括类外部的函数。
2. private
private
修饰符限制成员只能被同一类中的函数访问,类外部的函数无法直接访问这些成员。
3. protected
protected
修饰符类似于private
,但允许派生类(子类)访问基类(父类)的成员。
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
关键字表示变量的值不能被修改。
const int MAX_VALUE = 100;
// MAX_VALUE = 200; // 错误,不能修改const变量
2. volatile
volatile
关键字告诉编译器,变量的值可能会在程序的控制或检测之外被改变(如硬件直接修改内存)。
volatile int sensor_value; // 可能被硬件修改的变量
3. signed 和 unsigned
signed
允许变量存储正数和负数,而unsigned
只允许存储非负数。
signed int num1 = -10; // 可以存储负数
unsigned int num2 = 10; // 只能存储非负数
// unsigned int num3 = -5; // 错误的用法,会导致意外结果
4. short 和 long
这些修饰符改变整数类型的大小。
short int smallNumber = 32767; // 通常为2字节
long int bigNumber = 2147483647; // 通常为4字节
long long int veryBigNumber = 9223372036854775807LL; // 通常为8字节
存储类修饰符
存储类修饰符定义了变量和函数的范围(可见性)和生命周期。
1. auto
在C++11之前,auto
是默认的存储类,表示局部变量。在C++11及以后,auto
用于类型推导。
auto i = 10; // 编译器自动推导i为int类型
auto d = 10.5; // 编译器自动推导d为double类型
auto s = "Hello"; // 编译器自动推导s为const char*类型
2. register
register
修饰符建议编译器将变量存储在CPU寄存器中,以加快访问速度。
register int counter = 0; // 建议存储在寄存器中
在现代C++中,register
关键字基本被弃用,因为编译器的优化能力已经足够强大。
3. static
static
修饰符有两种用途:
- 当应用于局部变量时,它使变量在函数调用间保持其值。
- 当应用于全局变量或函数时,它将其可见性限制在当前文件内。
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
声明一个变量或函数在其他文件中定义,使当前文件可以使用它。
// 在一个文件中
extern int globalVar; // 声明变量在其他地方定义
extern void someFunction(); // 声明函数在其他地方定义
// 在另一个文件中
int globalVar = 100; // 实际定义
void someFunction() {
// 函数实现
}
5. mutable
mutable
允许const成员函数修改类中被声明为mutable的成员变量。
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:常量与引用结合
在实际开发中,我们经常需要传递大型对象作为函数参数。为了提高效率,我们可以使用常量引用:
// 传值方式(低效,会复制整个对象)
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:静态成员实现计数器
静态成员可以用来跟踪类的实例数量:
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:访问修饰符在继承中的应用
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++修饰符是编程语言的重要组成部分,正确使用修饰符可以:
- 控制类成员的访问权限(public、private、protected)
- 修改数据类型的特性(const、volatile、signed、unsigned等)
- 控制变量的存储方式和生命周期(static、extern、register等)
掌握这些修饰符的用法对于编写高效、安全和可维护的C++代码至关重要。在实际开发中,合理选择修饰符可以提高代码质量,减少错误,并增强性能。
练习
- 创建一个类,包含公共、私有和保护成员,然后创建一个子类,测试不同成员的可访问性。
- 编写一个函数,使用静态局部变量来计算该函数被调用的次数。
- 创建一个包含const成员和mutable成员的类,并演示如何在const成员函数中修改mutable成员。
- 实现一个单例模式,使用static和private访问修饰符确保只能创建一个类实例。
编程练习是巩固所学知识的好方法。尝试实现上述练习题目,这将帮助你更好地理解和应用C++修饰符。