全局静态变量、函数内静态变量、attribute((destructor))析构顺序

构造析构顺序的不确定性

以及静态函数获取的单例。

  • C++ 标准规定:同一个编译单元(同一个 cpp 文件)内,静态/全局对象的析构顺序与构造顺序相反。

  • 但不同编译单元(不同 cpp 文件/so)之间的析构顺序是未定义的。

  • 局部 static(即函数内 static)对象的析构顺序与其定义顺序有关,但也只在同一编译单元内有保证。

若一个变量仅在单个文件中可见,则建议将这个变量声明为静态全局变量,static修饰的静态全局变量仅在当前文件中可见

如果一个全局变量只被单个函数使用,将其改为该函数的静态局部变量可以进一步限制变量的作用域,提高代码的内聚性,降低耦合度。静态局部变量具有全局寿命但局部作用域的特点,

静态全局变量是存储在**静态数据区的,**而不是栈区,因此静态全局变量的大小不会导致栈溢出。栈溢出通常是由于函数调用层次过深或局部变量过大导致的。

类的内存占用

image-20250705165525325

1.32位系统中虚函数指针为4字节,64位为8字节

2.只需要考虑虚函数指针,虚函数表不计入某个类的资源

3.char占一字节,但是需要考虑内存调用

4.如果有虚继承,则多一个虚基类指针。

5.空类占一个字节(用于标识)

指针好题

image-20250707192757690

image-20250707192739357

int arr[5]={1,2,3,4,5};在这个数组的定义中,通常的理解arr是数组的地址即数组首元素的地址,进一步理解arr是一个int型的指针常量,常量+1地址偏移sizeof(int),所以arr+1是首元素下一个元素的地址;考虑到这一层就不难理解**&arr*的含义,&arr是对arr取地址,结果也是个地址,只是这个地址的类型是指向有5个int类型数据的数组的指针常量,这个常量+1地址偏移5sizeof(int)。

各级指针算各级的: 主要就是理解& 和 * 的“升级降级”;

链接:https://www.nowcoder.com/exam/test/89156461/submission?examPageSource=Intelligent&pid=62380309&testCallback=https%3A%2F%2Fwww.nowcoder.com%2Fexam%2Fintelligent%3FquestionJobId%3D10%26subTabName%3Dintelligent_page%26tagId%3D21000&testclass=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91

Copy and Swap

传统做法operator=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyString {
private:
char* data; // 动态分配的字符串
public:
// 赋值运算符重载
MyString& operator=(const MyString& other) {
// 检查自赋值
if (this == &other) {
return *this;
}

// 释放当前对象的资源
delete[] data;

// 复制数据
data = new char[std::strlen(other.data) + 1];
std::strcpy(data, other.data);

// 返回当前对象的引用
return *this;
}
};

Copy and Swap

1
2
3
4
5
6
7
8
9
MyString& operator=(const MyString& other) {
// 检查自赋值
if (this == &other) {
return *this;
}
MyString tmp{other};
std::swap(data,other.data);
return *this;
}

优势:

异常安全:传统方法new抛出异常时,对象出于无效状态。data已经被删除,但是分配失败

强异常安全性: 如果一个操作因为异常而失败,程序的状态会回滚到操作之前的样子,就像这个操作从来没执行过一样

代码复用:复用拷贝构造函数

自动资源管理:自动释放tmp资源

C++11 写法

1
2
3
4
5
6
MyString& operator=(MyString other) {//值传递
//传入左值:拷贝构造
//传入右值:移动构造
std::swap(data,other.data);
return *this;
}

移动构造还是拷贝构造?

左值:叫得出名字 右值:叫不出名字(临时变量,std::move)

左值用拷贝构造,右值用移动构造(偷)

模板

模板特化、偏特化

模板特化是为特定的模板参数提供特殊实现的技术。当通用模板不适合某些特定类型时,可以为其提供定制版本。

所有模板参数都指定具体类型就叫模板全特化

偏特化只对部分模板参数进行特化,函数模板不支持偏特化,只有类模板支持

函数模板特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <cstring>

// 通用模板
template<typename T>
void print(T value) {
std::cout << "General: " << value << std::endl;
}

// 全特化 - 为const char*类型
template<>
void print<const char*>(const char* value) {
std::cout << "C-string: \"" << value << "\"" << std::endl;
}

// 全特化 - 为int类型
template<>
void print<int>(int value) {
std::cout << "Integer: " << value << " (0x" << std::hex << value << ")" << std::endl;
}

int main() {
print(3.14); // 调用通用版本
print("Hello"); // 调用const char*特化
print(42); // 调用int特化
return 0;
}

类模板特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

// 通用模板
template<typename T>
class TypeInfo {
public:
static const char* name() { return "Unknown"; }
};

// 全特化 - int类型
template<>
class TypeInfo<int> {
public:
static const char* name() { return "int"; }
};

// 全特化 - double类型
template<>
class TypeInfo<double> {
public:
static const char* name() { return "double"; }
};

// 全特化 - const char*类型
template<>
class TypeInfo<const char*> {
public:
static const char* name() { return "const char*"; }
};

int main() {
std::cout << TypeInfo<float>::name() << std::endl; // Unknown
std::cout << TypeInfo<int>::name() << std::endl; // int
std::cout << TypeInfo<const char*>::name() << std::endl; // const char*
return 0;
}

偏特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
// 通用模板 - 两个类型参数
template<typename T, typename U>
class Pair {
public:
void print() { std::cout << "General Pair<T, U>" << std::endl; }
};

// 偏特化 - 两个类型相同
template<typename T>
class Pair<T, T> {
public:
void print() { std::cout << "Specialized Pair<T, T>" << std::endl; }
};

// 偏特化 - 第二个参数为int
template<typename T>
class Pair<T, int> {
public:
void print() { std::cout << "Specialized Pair<T, int>" << std::endl; }
};

int main() {
Pair<double, char> p1; p1.print(); // General Pair<T, U>
Pair<float, float> p2; p2.print(); // Specialized Pair<T, T>
Pair<std::string, int> p3; p3.print(); // Specialized Pair<T, int>
return 0;
}


//指针类型进行偏特化
// 通用模板
template<typename T>
class Wrapper {
public:
static const char* type() { return "Value"; }
};

// 偏特化 - 指针类型
template<typename T>
class Wrapper<T*> {
public:
static const char* type() { return "Pointer"; }
};

// 偏特化 - 指向指针的指针
template<typename T>
class Wrapper<T**> {
public:
static const char* type() { return "Pointer-to-Pointer"; }
};

int main() {
std::cout << Wrapper<int>::type() << std::endl; // Value
std::cout << Wrapper<int*>::type() << std::endl; // Pointer
std::cout << Wrapper<int**>::type() << std::endl; // Pointer-to-Pointer
return 0;
}

可变参数模板

基本用法:

1
2
3
4
template<typename... Args> // Args是模板参数包
void function(Args... args) { // args是函数参数包
// 处理参数...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

// 基础情况:0个参数时返回0
template<typename T>
T sum(T value) {
return value;
}

// 递归求和
template<typename T, typename... Args>
T sum(T first, Args... args) {
return first + sum(args...);
}

// 计算参数个数
template<typename... Args>
std::size_t count(Args... args) {
return sizeof...(args); // sizeof... 操作符获取参数包大小
}

int main() {
std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 15
std::cout << sum(1.5, 2.5, 3.5) << std::endl; // 7.5
std::cout << count(1, 'a', "hello", 3.14) << std::endl; // 4
return 0;
}

非类型模板参数(常量模板)

cuda中用的多,用于编译器常量优化

1
2
3
4
5
6
7
8
9
10
template <int N>
int sumArray(int (&arr)[N]) {
int sum = 0;
for (int i = 0; i < N; i++)
sum += arr[i];
return sum;
}

int a[5] = {1,2,3,4,5};
int total = sumArray(a); // N=5,编译期确定

类型转换运算符

static_cast 静态转换

用于在编译时已知的,有逻辑关联的类型之间的转换

场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//基本数据类型之间的转换
int i = 10;
double d = static_cast<double>(i); // int -> double

//派生类指针/引用 -> 基类指针/引用(向上转换,Upcasting)。这是安全的,并且是隐式转换的显式写法。
class Base {};
class Derived : public Base {};
Derived derived;
Base* basePtr = static_cast<Base*>(&derived); // 安全

//基类指针/引用 -> 派生类指针/引用(向下转换,Downcasting)。不安全
Base* basePtr = new Derived(); // 实际上指向一个Derived对象
Derived* derivedPtr = static_cast<Derived*>(basePtr); // 可以,但有风险

//任何具有转换构造函数的类型转换
class MyClass {
public:
MyClass(int x) {} // 转换构造函数
};
int num = 5;
MyClass obj = static_cast<MyClass>(num);

dynamic_cast动态转换

用于安全的在继承参差结构中进行向下转换或者交叉转换,依赖运行时类型信息RTTI

用法: 将基类指针或引用安全的转换为派生类的指针或者引用,必须用于多态类型(至少含有一个虚函数)

​ 转换成功会返回目标类型的指针,转换失败返回null或者std::bad_cast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Base { virtual void foo() {} }; // 多态基类(有虚函数)
class Derived : public Base {};

Base* basePtr = new Derived(); // 正确指向派生类

// 安全向下转换
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr != nullptr) {
// 转换成功,可以使用derivedPtr
} else {
// 转换失败
}

Base* basePtr2 = new Base(); // 指向基类本身
Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2);
// derivedPtr2 将是 nullptr!

// 引用转换
try {
Derived& derivedRef = dynamic_cast<Derived&>(*basePtr);
} catch (const std::bad_cast& e) {
std::cout << "转换失败: " << e.what() << std::endl;
}

const_cast 常量转换

用于修改类型的 constvolatile 属性。这是唯一能“去掉” const 属性的转换操作符

主要使用方式就是去掉const属性,以便接受一个非const参数但是不会修改还旧API

1
2
3
4
5
6
7
void printString(char* str) { // 一个旧的、不修改str的函数
std::cout << str << std::endl;
}

const char* myStr = "Hello, World!";
// printString(myStr); // 错误:不能将const char* 传递给char*
printString(const_cast<char*>(myStr)); // 可行,但有风险

reinterpret_cast 重新解释转换

进行低级的、底层的、“重新解释”比特模式的转换。它可以将一个指针转换为任何其他类型的指针,甚至是一个整数。

不进行任何格式检查或安全性检查,可移植性差

场景

1
2
3
4
5
6
7
//在函数指针类型之间进行转换。
//在指针和足够大的整数类型之间进行转换(如 void* 转 uintptr_t)。

int* ip = new int(65);
// 将int指针毫无关系地转换为char指针,然后打印其指向的值
char* cp = reinterpret_cast<char*>(ip);
std::cout << *cp; // 输出 'A' (因为65是'A'的ASCII码)

本站由 Zane Jiang 使用 Stellar 1.33.1 主题创建,一款很棒的 Hexo 主题!

总访问 次 || 本页访问
总访客 人 || 本页访客