第二十六天:static、const、#define的用法和区别
static、const、#define的用法和区别
1. static
的用法
- 修饰局部变量:当
static
修饰局部变量时,该变量的生命周期会延长至整个程序运行期间,但其作用域仍局限于声明它的函数内部。每次函数调用时,其值会保留上次调用结束时的值。例如:
#include <iostream>
void test() {static int count = 0;count++;std::cout << "Count: " << count << std::endl;
}
int main() {for (int i = 0; i < 3; i++) {test();}return 0;
}
在上述代码中,count
是一个静态局部变量,每次调用test
函数时,count
的值都会增加,并且在函数调用之间保持其值。输出结果为:
Count: 1
Count: 2
Count: 3
- 修饰全局变量:当
static
修饰全局变量时,该变量的作用域被限制在定义它的源文件内,其他源文件无法访问。这有助于避免全局变量命名冲突。例如,在file1.cpp
中定义static int globalStaticVar = 10;
,在file2.cpp
中无法直接访问globalStaticVar
。 - 修饰成员变量:在类中,
static
修饰的成员变量为所有类对象所共享,不属于任何一个具体对象。它在类的所有对象之间是唯一的,存储在全局数据区,而不是对象的内存空间中。例如:
#include <iostream>
class MyClass {
public:static int sharedVar;
};
int MyClass::sharedVar = 0;
int main() {MyClass obj1, obj2;obj1.sharedVar = 1;std::cout << "obj1.sharedVar: " << obj1.sharedVar << std::endl;std::cout << "obj2.sharedVar: " << obj2.sharedVar << std::endl;return 0;
}
在上述代码中,sharedVar
是MyClass
类的静态成员变量,通过一个对象修改sharedVar
的值,其他对象访问时也会看到相同的变化。输出结果为:
obj1.sharedVar: 1
obj2.sharedVar: 1
- 修饰成员函数:
static
修饰的成员函数只能访问静态成员变量和其他静态成员函数,不能访问非静态成员变量,因为非静态成员变量依赖于具体的对象实例,而静态成员函数不依赖于任何对象实例。例如:
#include <iostream>
class MyClass {
public:static int sharedVar;static void staticFunction() {sharedVar++;std::cout << "Shared variable in static function: " << sharedVar << std::endl;}
};
int MyClass::sharedVar = 0;
int main() {MyClass::staticFunction();return 0;
}
在上述代码中,staticFunction
是一个静态成员函数,它可以访问和修改静态成员变量sharedVar
。
2. const
的用法
- 修饰变量:
const
修饰的变量为常量,其值一旦初始化后就不能再改变。例如:
const int num = 10;
// num = 20; // 错误,不能修改常量的值
- 修饰指针:
const
修饰指针时有两种情况,一种是指向常量的指针(const int* ptr
),指针指向的值不能通过该指针修改,但指针本身可以指向其他地址;另一种是常量指针(int* const ptr
),指针本身不能再指向其他地址,但指针指向的值可以修改。例如:
const int num1 = 10;
int num2 = 20;
const int* ptr1 = &num1;
// *ptr1 = 30; // 错误,不能通过指向常量的指针修改值
ptr1 = &num2; // 正确,指针可以指向其他地址int* const ptr2 = &num2;
*ptr2 = 30; // 正确,常量指针指向的值可以修改
// ptr2 = &num1; // 错误,常量指针不能再指向其他地址
- 修饰成员函数:在类中,
const
修饰的成员函数不能修改类的非静态成员变量,常用于只读取数据而不修改数据的函数。例如:
#include <iostream>
class MyClass {
private:int data;
public:MyClass(int value) : data(value) {}int getData() const {// data = 10; // 错误,不能在 const 成员函数中修改非静态成员变量return data;}
};
int main() {MyClass obj(5);std::cout << "Data: " << obj.getData() << std::endl;return 0;
}
在上述代码中,getData
是一个const
成员函数,它保证不会修改MyClass
类的非静态成员变量data
。
- 修饰对象:
const
修饰的对象只能调用const
成员函数,因为非const
成员函数可能会修改对象的状态。例如:
const MyClass constObj(10);
// constObj.getData(); // 正确,调用 const 成员函数
// constObj.setData(20); // 错误,如果有 setData 非 const 成员函数,不能调用
3. #define
的用法
- 定义常量:
#define
可以用来定义宏常量,在预处理阶段,预处理器会将代码中出现的宏常量替换为其定义的值。例如:
#define PI 3.14159
#include <iostream>
int main() {double radius = 5.0;double area = PI * radius * radius;std::cout << "Area of circle: " << area << std::endl;return 0;
}
在上述代码中,PI
是一个宏常量,预处理器会在编译前将代码中的PI
替换为3.14159
。
- 定义宏函数:
#define
还可以定义宏函数,它与普通函数类似,但在预处理阶段进行文本替换,没有函数调用的开销。例如:
#define MAX(a, b) ((a) > (b)? (a) : (b))
#include <iostream>
int main() {int num1 = 5, num2 = 10;int maxVal = MAX(num1, num2);std::cout << "Max value: " << maxVal << std::endl;return 0;
}
在上述代码中,MAX
是一个宏函数,预处理器会将MAX(num1, num2)
替换为((num1) > (num2)? (num1) : (num2))
。
4. 三者的区别
- 作用域和生命周期:
static
修饰的局部变量作用域在函数内,生命周期贯穿整个程序;修饰的全局变量作用域在源文件内,生命周期也是整个程序;静态成员变量和函数属于类,生命周期与程序相同。const
修饰的变量作用域取决于其定义位置,常量在其作用域内保持不变,生命周期与其作用域一致。#define
定义的宏常量和宏函数作用域从定义处到文件结束,在预处理阶段进行替换,没有实际的作用域和生命周期概念。
- 类型检查:
static
和const
都有类型,编译器会进行类型检查。#define
只是简单的文本替换,没有类型检查,可能会导致一些不易察觉的错误,例如#define ADD(a, b) a + b
,如果调用ADD(3, 5) * 2
,实际会被替换为3 + 5 * 2
,结果与预期不同。
- 内存分配:
static
修饰的局部变量和全局变量都在全局数据区分配内存;静态成员变量在类的静态存储区分配内存。const
修饰的常量如果是基本类型且在编译期可以确定值,可能会被优化到常量存储区,否则在栈上(局部常量)或全局数据区(全局常量)分配内存。#define
定义的宏在预处理阶段替换,不占用运行时内存。
- 可调试性:
static
和const
常量在调试时可以正常显示和跟踪。#define
宏在预处理后就不存在了,调试时很难跟踪宏替换后的实际代码。