static
1、静态成员变量
静态成员变量在类声明之后、类外进行定义并初始化,初始化完成后,非const类型的静态成员变量的值在程序运行时可以被改变,而const类型的静态成员变量其值则固定不变。
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
2、静态成员函数
- 所有对象共享同一个函数
- 只能访问静态成员变量
- 不可以访问非静态成员变量
3、静态全局变量
- 如果全局变量被声明为静态,它的作用域将被限制在定义它的源文件中。这意味着,尽管静态全局变量在整个程序执行期间都存在,但它只对定义它的文件是可见的,其他源文件中的代码无法直接访问它。这有助于避免命名冲突并减少全局变量的副作用。
- 生命周期:整个程序运行期间。
- 作用域:定义它的源文件内。
4、静态普通函数
- 在C++中,静态函数还可以用于限制函数的作用域到定义它的源文件中,类似于静态全局变量。这意味着即使在不同的源文件中有同名函数,只要它们是静态的且位于不同文件,就不会产生链接错误。
- 作用域:定义它的源文件内。
- 静态普通函数通常用于实现一些全局性的功能,这些功能与特定的类或对象实例无关,但又希望保持其作用域限制在较小范围内,以减少对外部的影响或避免命名冲突。例如,某些辅助工具函数、日志记录函数或者配置文件的读取函数等,可能适合以静态普通函数的形式实现。
静态普通函数是C++中一种用于实现特定功能、限制作用域同时具有较长生命周期的函数,适用于那些不需要与特定对象交互的通用逻辑或工具方法。
5、静态局部变量
- 当
static
用于局部变量时,它改变了变量的生命周期。通常,局部变量在函数调用结束后会被销毁,但静态局部变量则会在第一次初始化后持续整个程序的生命周期,即使函数结束调用后依然存在。
- 它的作用域仍然限于定义它的函数内部。
- 生命周期:程序运行期间一直存在。
- 作用域:定义该变量的函数内部。
const
const首先作用于左边,若左边没有东西,就作用于右边
指针常量与常量指针在C++中是两个容易混淆的概念,但它们有着本质的区别:
指针常量(Pointer Constants)
指针常量是指一个指针变量,其自身的地址(即指向的位置)在初始化后就不能再改变。换句话说,这个指针一旦指向了一个内存地址,之后就不能再让它指向另一个地址。但是,指针所指向的数据是可以修改的(如果该数据不是常量的话)。在声明时,关键字const
位于星号(*
)的右侧,靠近指针变量名。
语法示例:
int* const ptr = &someValue;
在这个例子中,ptr
是一个指向整数的指针常量,它初始化后就不能再指向其他地方,但是可以通过ptr
修改someValue
的值(如果someValue
不是常量)。
常量指针(Constant Pointers)
常量指针是指向常量的指针,即通过这个指针不能修改它所指向的数据,但可以改变指针自身指向的地址。这意味着,虽然指针指向的内存位置的内容是不可变的,但指针可以被重新设置去指向另一个内存位置。在声明时,关键字const
位于星号(*
)的左侧,靠近数据类型。
语法示例:
const int* ptr = &someValue;
这里,ptr
是一个常量指针,它指向的整数是不可修改的,但ptr
可以被重新指向另一个整数的地址。
指向常量的常量指针(Constant Pointer to Constant)
这种情况下,指针既不能指向新的地址,也不能修改它所指向的数据。
语法示例:
const int* const ptr = &someValue; // ptr和ptr指向的int都是常量
在这个例子中,ptr
是一个指向常量整数的常量指针,既不能改变它所指向的地址,也不能通过它修改someValue
。
const与函数
在C++中,const
关键字有多种用途,其中之一是在函数声明和定义中使用,以指明该函数不会修改类的任何成员变量(对于成员函数而言)或者输入参数(对于普通函数而言)。这种情况下,const
有助于表达函数的意图,提高代码的可读性和安全性。下面分别从成员函数和普通函数两个角度来讲解const
在只读函数中的应用。
成员函数中的const
- 常量成员函数:当一个成员函数被声明为
const
时,表明这个函数不会修改类的任何成员变量(除了那些用mutable
关键字标记的成员)。这为调用者提供了一个保证,即这个操作是安全的,不会影响对象的状态。
class MyClass {
public:
void display() const { // 常量成员函数
cout << "Value: " << value << endl;
}
int getValue() const { // 另一个常量成员函数示例
return value;
}
private:
int value;
};
在上述例子中,display()
和getValue()
都是常量成员函数,它们不会修改MyClass
对象的状态。
- const后置修饰符:在C++中,成员函数的
const
修饰符习惯上放置在函数参数列表之后,这是C++的一个特殊语法约定。尽管它看起来像是返回类型的一部分,但实际上它修饰的是函数本身,表明这是一个“不会改变对象状态”的版本。
普通函数中的const
在非成员函数(即普通函数)中,const
关键字通常用于指针或引用类型的参数,以表明该参数在函数内部会被当作常量处理,不会被修改。
void printValue(const int& val) { // val不会被修改
cout << "Value: " << val << endl;
}
void modifyArray(int* arr, size_t size) {
for(size_t i = 0; i < size; ++i) {
arr[i] *= 2; // 修改数组内容
}
}
void printArray(const int* arr, size_t size) { // 指向const的指针,arr内容不可修改
for(size_t i = 0; i < size; ++i) {
cout << arr[i] << " ";
}
cout << endl;
}
在上面的例子中,printValue
接受一个指向整数的常量引用,保证了传入的值不会被函数修改。同样地,printArray
接收一个指向const整数的指针,表明它只读取数组内容,不会进行修改。
总结
通过在函数声明和定义中使用const
关键字,可以明确地告诉编译器及程序员哪些函数或参数是只读的,这对于维护代码的稳定性和理解代码逻辑非常有帮助。编译器也会根据这些信息进行类型检查,确保不违反const限制,从而减少潜在的错误。
- 指针常量关注的是指针本身不可变,即指针的地址不可更改。
- 常量指针关注的是指针所指向的数据不可变,但指针本身可以指向其他地方。
- 指向常量的常量指针(Constant Pointer to Constant):指针指向的数据不可变,同时指针自身也不可变。
- 修饰函数的参数,表示该参数不可更改。
- 修饰类的成员函数,表示该成员函数不能修改任何成员变量。
- 修饰类的成员变量,表示该成员不能被修改,且只能通过初始化列表的方式初始化。
- 修饰类的对象的整体,也即常量对象,表示该对象的所有成员变量都不能被修改,且只能调用被const修饰的成员函数。
explicit
在C++中,explicit
关键字主要用于限制编译器执行隐式类型转换的能力,特别是在单个参数的构造函数或者转换函数上。它的主要目的是为了防止意外的、不易察觉的类型转换,提高代码的清晰度和安全性。下面详细解释explicit
的使用场景和作用。
使用场景
- 构造函数:当一个类的构造函数只有一个参数时,编译器会自动允许将该类型的值隐式转换为类的对象。这在某些情况下可能导致意料之外的转换。通过将这样的构造函数声明为
explicit
,你可以禁止这种隐式转换。
class Inch {
public:
explicit Inch(int i) : value(i) {} // explicit构造函数
private:
int value;
};
void processInch(Inch i) {}
int main() {
// 下面的语句是错误的,因为不能隐式转换
// processInch(5);
// 必须显式构造Inch对象
processInch(Inch(5));
}
- 转换函数:类似地,如果一个类定义了转换运算符(conversion operator)到其他类型,也可以使用
explicit
关键字来限制隐式转换。
class Integer {
public:
explicit operator double() const { return value; }
private:
int value;
};
作用
- 避免误用:通过阻止不必要的隐式类型转换,
explicit
关键字可以帮助预防编程错误,比如错误的数据类型假设。 - 提高代码可读性:它使得类型转换更加显式,让阅读代码的人更容易理解类型转换的意图,增加了代码的可读性和可维护性。
- 性能考量:虽然这不是
explicit
的主要目的,但在某些情况下,避免无意识的构造和转换操作可能对性能有正面影响,尤其是当这些操作成本较高时。
总的来说,explicit
关键字是一个强大的工具,用于控制和限制C++中的类型转换,帮助开发者编写更安全、更易于理解和维护的代码。
final
final
是一个关键字,用于修饰类或成员函数,以防止它们被继承或重写。
- 修饰类:当
final
关键字用于修饰一个类时,这意味着该类不能被其他类继承。任何尝试继承final
修饰的类的操作都会导致编译错误。
示例:
class Base final {
// ...
};
class Derived : public Base { // 编译错误,Base是final的
// ...
};
- 修饰成员函数:当
final
关键字用于修饰一个成员函数时,这意味着该函数在派生类中不能被重写。任何尝试重写final
修饰的成员函数的操作都会导致编译错误。
示例:
class Base {
public:
virtual void func() final {
// ...
}
};
class Derived : public Base {
public:
void func() override { // 编译错误,func是final的
// ...
}
};
使用final
关键字可以增强代码的安全性,因为它可以防止意外的继承或重写,从而确保类的行为符合设计者的预期。