c++中的static
在C++编程语言中,static关键字有几种不同的用途,具体取决于它所修饰的对象或函数:
- 静态局部变量:
当static用于局部变量时,它改变了局部变量的存储期限。通常,局部变量在函数调用结束后就不存在了,但静态局部变量在程序执行期间一直存在,直到程序结束。它们的初始值只设置一次,并且在函数调用之间保持它们的值。
- 静态成员变量:
在类中,static关键字可以用来声明类的成员变量。这样的变量不是属于类的某个具体对象的,而是被类的所有对象共享。无论创建了多少个类的对象,静态成员变量只有一个副本。
- 静态成员函数:
类的成员函数也可以被声明为static。静态成员函数不依赖于类的对象,它们可以独立于任何对象而被调用。静态成员函数只能访问静态成员变量和其他静态成员函数。
- 静态全局变量:
在全局作用域中,static关键字用来声明一个文件内的全局变量。这样的变量只在定义它的文件内可见,不会与其他文件中的同名变量冲突。
- 静态函数:
在全局作用域中,static关键字可以用来声明一个函数,使得该函数只在定义它的文件内可见。这有助于避免在其他文件中同名函数的冲突。
static关键字在C++中的使用非常灵活,可以用于控制变量和函数的作用域、生命周期和链接属性。正确使用static可以增强程序的模块性和安全性。
- static修饰类的函数成员,不能在该函数中使用this指针,也不能使用非静态数据成员
- static修饰类的成员函数 先于 类的对象存在,如果是公有静态函数可以在类的外部直接通过类名调用
C++中的单例
单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。在C++中,实现单例模式通常涉及以下步骤:
1. 将构造函数设为私有,以防止外部通过new关键字创建类的实例。
2. 在类中定义一个静态的私有成员变量,用于存储单例实例。
3. 提供一个静态的公有方法(通常称为`GetInstance`),用于获取单例实例。如果实例尚未创建,则在该方法中创建实例。
class Singleton {
public:
// 获取单例实例的静态方法
static Singleton& GetInstance() {
static Singleton instance; // 局部静态变量,线程安全(C++11起)
return instance;
}
// 禁止复制构造函数和赋值操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 示例方法
void ShowMessage() {
std::cout << "Hello from Singleton!" << std::endl;
}
private:
// 私有构造函数,防止外部创建实例
Singleton() {
// 初始化操作
}
// 私有静态成员变量,用于存储单例实例
static Singleton* m_Instance;
};
// 初始化静态成员变量
// Singleton* Singleton::m_Instance = nullptr; // 如果需要懒汉式初始化,则需要此行
int main() {
Singleton::GetInstance().ShowMessage();
}
//其中 = delete;是一个特殊标识符 显式地禁用某个函数
在C++中,= delete是一个特殊的标识符,用于显式地禁用某个函数。当你在构造函数、拷贝构造函数、赋值运算符或其他成员函数后面加上= delete时,你是在告诉编译器,你不希望这个函数被使用,如果有人尝试调用它,编译器应该报错。
通过删除拷贝构造函数和赋值运算符,单例类保证了其实例的唯一性,因为不能通过这些操作来创建或复制实例。这样,唯一能够获取单例实例的方式就是通过GetInstance静态方法。
const关键字
在C++中,const关键字用于声明一个变量、对象或函数参数为常量,意味着它们的值在初始化后不能被修改。const的使用可以提供额外的类型安全性和清晰的代码意图。以下是const在不同上下文中的使用方式:
-
常量变量: 使用
const声明的变量必须在声明时初始化,之后不能更改其值。const int max_value = 100; // max_value = 200; // 错误,不能修改常量变量的值 -
常量引用:
const可以用来创建对变量的常量引用,这意味着通引用不能修改所引用的值。int value = 42; const int& ref = value; // ref = 43; // 错误,不能通过常量引用修改值 -
常量指针:
const可以用于指针,有两种用法:指向常量的指针和常量指针。-
指向常量的指针(pointer to const)不能用于修改其所指向的数
int value = 42; const int* ptr = &value; // *ptr = 43; // 错误,不能通过指向常量的指针修改数据 -
常量指针(const pointer)是指针本身的值是常量,即不能更改指针所指向的地址。
int value = 42; int* const ptr = &value; // ptr = nullptr; // 错误,不能修改常量指针的值
-
-
函数参数: 函数的参数可以使用
const来保证在函数内部不会修改传入的参数。void print(const std::string& message) { // 函数内部不能修改message的值 } -
成员函数: 类的成员函数可以使用
const修饰符来表明该函数不会修改类的成员变量。这样的函数被称为常量成员class MyClass { public: void modify() { // 可以修改成员变量 } void showInfo() const { // 不能修改成员变量 } }; -
常量表达式和编译时常量: 在C++11及以后的版本中,
const可以与constexpr关键字结合使用,来声明一个在编译时就可以确定的常量表达式。constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } const int result = factorial(5); // 在编译时计算使用
const可以提高代码的可读性和维护性,同时可以帮助编译器进行更多的优化。在编写C++代码时,应当尽可能使用const来保护不应该被修改的数据。
const 成员函数
在C++中,将类成员函数声明为const的目的是告诉编译器这个函数不会修改类的任何非静态成员变量。这样的函数被称为常量成员函数或const成员函数。通过声明为const,你可以确保函数只读取对象的状态,而不是修改它。
class MyClass {
public:
MyClass(int x) : x_(x) {}
// 常量成员函数
int getValue() const {
return x_;
}
// 非常量成员函数
void setValue(int value) {
x_ = value;
}
private:
int x_;
};
getValue是一个const成员函数,它保证不修改类的任何成员变量。这意味着你可以在常量对象上调用
const MyClass obj(42);
std::cout << obj.getValue() << std::endl; // 正确,可以在常量对象上调用const成员函数
然而,你不能在常量对象上调用非常量成员函数,因为这些函数可能会修改对象的状态:
const MyClass obj(42); //常量对象
// obj.setValue(43); // 错误,不能在常量对象上调用非常量成员函数
如果你尝试在const成员函数中修改成员变量,编译器将会报错:
class MyClass {
public:
// 错误,尝试在const成员函数中修改成员变量
void incorrectFunction() const {
x_ = 10; // 编译错误
}
};
==const成员函数的一个重要用途是在函数重载==中使用。你可以有一个成员函数的const版本和非const版本,编译器会根据对象的类型选择合适的版本:
class MyClass {
public:
void doSomething() {
// 非const版本的实现
}
void doSomething() const {
// const版本的实现
}
};
在这个例子中,如果对象是const的,那么const版本的doSomething会被调用;如果对象不是const的,那么非const版本的doSomething会被调用。
总之,const成员函数是C++中的一种机制,用于确保成员函数不会修改对象的状态,从而提高代码的安全性和可读性。
类型转换
C语言中的类型转换
在C语言中,类型转换通常是通过强制类型转换实现的,强制类型转换有两种形式:
-
传统强制类型转换 (C-style cast)
(type) expression double d = 3.14; int i = (int)d;这种方式可以用于任何类型的转换,但它不区分整数提升、浮点数转换、指针转换等,可能会导致潜在的问题。
-
函数风格强制类型转换 (Function-style cast)
type (expression) void func(double d) {std::cout << d << std::endl;} int main() { int i = 42; func(i); // i会被隐式转换为double return 0; }
C++中的类型转换
😡C++在C的基础上加了四种类型转换操作符,以提供更明确和安全的类型转换:
-
static_cast标准转换static_cast<type>(expression) int i = 42; double d = static_cast<double>(i); // 将int转换为doublestatic_cast可以在相关类型之间进行转换,它可以在编译时进行检查。它通常用于执行标准转换,如基本数据类型之间的转换、类层次结构中上下转换(只要没有虚继承),以及用户定义的类型转换(通过重载类型转换运算符)。 -
😡
dynamic_castdynamic_cast<type>(expression) class Base { public: virtual void print() const { std::cout << "Base" << std::endl; } }; class Derived : public Base { public: void print() const override { std::cout << "Derived" << std::endl; } }; Base* basePtr = new Derived(); Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); 对象的向下转型 if (derivedPtr) { derivedPtr->print(); // 输出 "Derived" } else { std::cout << "Cast failed" << std::endl; }dynamic_cast主要用于对象的向下转型(从基类指针到派生类指针)和多态类型转换。它会在运行时检查类型是否正确。如果转换是不可能的,例如,转换的对象不是目标类型的实例,那么dynamic_cast会返回空指针(对于指针类型)或抛出std::bad_cast异常(对于引用类型)。 -
reinterpret_cast低级转换reinterpret_cast<type>(expression) int i = 42; int* p = &i; void* voidPtr = reinterpret_cast<void*>(p); // 将int*转换为void*reinterpret_cast用于低级转换,它会重新解释位模式,通常用于将指针转换为整数或反之。这种转换是非常危险的,因为它不进行任何类型检查,只是简单地将位模式从一个类型复制到另一个类型。 -
const_castconst_cast<type>(expression) const char* constStr = "Hello, World!"; char* nonConstStr = const_cast<char*>(constStr); // 去除const属性 // 注意:修改通过const_cast去除const属性后的指针指向的内容是未定义行为const_cast用于去除或添加const属性。它只能用于改变表达式的const或volatile属性,不能用于其他类型的转换。
隐式类型转换
在C和C++中,编译器会在某些情况下自动执行类型转换,这称为隐式类型转换。隐式类型转换发生在以下几种情况:
- 算术转换:
- 当不同类型的值在表达式中进行运算时,编译器会将它们转换为共同的类型。例如,
int和float相加时,int会被转换为float。
- 当不同类型的值在表达式中进行运算时,编译器会将它们转换为共同的类型。例如,
- 赋值转换:
- 当将一个值赋给另一个类型的变量时,如果可能,编译器会自动转换类型。
- 函数调用转换:
- 当传递参数给函数时,如果参数类型与形参类型不匹配,编译器会尝试将实参转换为形参类型。
- 数组到指针转换
数组到指针转换:在大多数情况下,数组名会被自动转换为指向数组第一个元素的指针。
int arr[10];
int* ptr = arr; // arr被隐式转换为int*
- 整型提升:
char c = 'A';
int i = c; // c被隐式转换为int
//char short 等类型会被自动提升为int或更大的整数类型。
自定义数据类型
隐式类型转换的风险一般存在于自定义类型转换间。尤其需要注意自定义类的构造函数。例如:
class MyString
{
public:
MyString(int n) {} // 本意:预先分配n个字节给字符串
MyString(const char* p) {} // 用C风格的字符串p作为初始化值
};
void main()
{
MyString s1 = "China"; //隐式转换,等价于MyString s1 = MyString("China")
MyString s2(10); //分配10个字节的空字符串
MyString s3 = MyString(10); //分配10个字节的空字符串
MyString s4 = 10; //也是分配10个字节的空字符串
MyString s5 = 'A'; //分配int('A')个字节的空字符串
// s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。
}
要想禁止隐式类型转换,可以使用C++关键字explicit。
#include<iostream>
using namespace std;
class MyString
{
public:
//explicit 用途: 防止隐式类型转换方式 来初始化对象
explicit MyString(int n) {
cout<<"MyString(int n)"<<endl;
} // 本意:预先分配n个字节给字符串
MyString(const char* p) {
cout<<"MyString(const char* p)"<<endl;
} // 用C风格的字符串p作为初始化值
};
int main()
{ MyString s4 = 1008611;
//1、在没有加explicit关键字之前,编译器会自动进行隐式类型转换 MyString s4 = MyString(1008611); 编译通过
//2、在加了explicit关键字之后,编译器不会给你做类型转换,此时编译错误,目的是告诉程序员不要使用这种写法,因为这种写法会让别人误解
return 0;
}
本文详细介绍了C++中static关键字的不同用途,包括静态局部变量、静态成员变量、静态成员函数、静态全局变量和单例模式。此外,还探讨了const关键字在声明常量和限制修改的重要性,以及类型转换和explicit关键字的使用。

被折叠的 条评论
为什么被折叠?



