面试题答案
一键面试全局变量
- 生命周期与相互作用
- 模板元编程:模板元编程在编译期进行计算,不会直接影响全局变量的生命周期。但如果全局变量的类型是由模板实例化而来,模板元编程会决定其类型的具体特性。例如,假设有一个模板类
MyAllocator
用于自定义内存分配器:
template <typename T> class MyAllocator { // 自定义内存分配逻辑 }; template <typename T> using MySmartPtr = std::unique_ptr<T, MyAllocator<T>>; class MyClass {}; MySmartPtr<MyClass> globalPtr; // 全局智能指针变量,类型由模板实例化得到
- 智能指针:如果全局变量是智能指针,它会在程序启动时初始化(按照全局变量初始化顺序),并且在程序结束时,智能指针的析构函数会被调用,释放其所管理的资源。例如上述
globalPtr
,如果它指向了某个MyClass
对象,在程序结束时会调用MyAllocator<MyClass>
的释放函数来释放内存。 - 自定义内存分配器:如果全局变量使用了自定义内存分配器(如上述
MySmartPtr
使用MyAllocator
),其内存的分配和释放会遵循自定义内存分配器的规则。在全局变量的构造和析构过程中,会调用自定义内存分配器的allocate
和deallocate
函数。
- 模板元编程:模板元编程在编译期进行计算,不会直接影响全局变量的生命周期。但如果全局变量的类型是由模板实例化而来,模板元编程会决定其类型的具体特性。例如,假设有一个模板类
- 潜在问题
- 初始化顺序问题:如果一个全局变量依赖于另一个全局变量的初始化结果,可能会因为初始化顺序不当导致未定义行为。例如:
这里如果MySmartPtr<MyClass> globalPtr1; MySmartPtr<MyClass> globalPtr2(globalPtr1.get()); // globalPtr2 依赖 globalPtr1 的初始化
globalPtr1
还未初始化,globalPtr2
的构造就会导致未定义行为。- 内存泄漏:如果自定义内存分配器在全局变量析构时没有正确释放内存,可能会导致内存泄漏。例如自定义内存分配器的
deallocate
函数实现有误。
- 解决方案
- 初始化顺序问题:尽量避免全局变量之间的相互依赖。如果无法避免,可以使用
std::call_once
来确保关键的全局变量在需要时被正确初始化。例如:
std::once_flag globalInitFlag; MySmartPtr<MyClass>& getGlobalPtr1() { static MySmartPtr<MyClass> ptr; std::call_once(globalInitFlag, []() { ptr.reset(new MyClass()); }); return ptr; } MySmartPtr<MyClass> globalPtr2(getGlobalPtr1().get());
- 内存泄漏:仔细检查自定义内存分配器的实现,特别是
deallocate
函数。可以编写单元测试来验证内存分配和释放的正确性。
- 初始化顺序问题:尽量避免全局变量之间的相互依赖。如果无法避免,可以使用
局部变量
- 生命周期与相互作用
- 模板元编程:同样,模板元编程在编译期决定局部变量的类型特性,但不影响其生命周期。例如:
void localFunction() { MySmartPtr<MyClass> localPtr; // 局部智能指针变量,类型由模板实例化得到 }
- 智能指针:局部智能指针变量在进入其作用域时被初始化,在离开作用域时其析构函数被调用,释放所管理的资源。例如上述
localPtr
,当localFunction
函数结束时,会调用MyAllocator<MyClass>
的释放函数。 - 自定义内存分配器:局部变量使用自定义内存分配器时,内存的分配和释放也遵循自定义内存分配器的规则。在局部变量的构造和析构过程中,调用自定义内存分配器的
allocate
和deallocate
函数。
- 潜在问题
- 作用域管理不当:如果在局部作用域内不正确地传递智能指针,可能导致意外的资源释放。例如:
这里如果调用者没有正确处理返回的MySmartPtr<MyClass> localFunction() { MySmartPtr<MyClass> localPtr(new MyClass()); MySmartPtr<MyClass> otherPtr = localPtr; // localPtr 失去对资源的所有权 return otherPtr; }
otherPtr
,可能会导致资源提前释放或未释放。- 内存泄漏:类似于全局变量,如果自定义内存分配器在局部变量析构时没有正确释放内存,会导致内存泄漏。
- 解决方案
- 作用域管理不当:理解智能指针的所有权转移规则。使用
std::move
来显式转移所有权,避免意外的资源释放。例如:
MySmartPtr<MyClass> localFunction() { MySmartPtr<MyClass> localPtr(new MyClass()); return std::move(localPtr); }
- 内存泄漏:同全局变量,仔细检查自定义内存分配器的实现,并编写单元测试验证。
- 作用域管理不当:理解智能指针的所有权转移规则。使用
在C++标准库中,智能指针(如 std::unique_ptr
、std::shared_ptr
)的实现遵循严格的资源管理规则。std::unique_ptr
使用独占所有权模型,析构时释放资源。std::shared_ptr
使用引用计数,当引用计数为0时释放资源。自定义内存分配器需要满足标准库对分配器的要求,如 allocate
、deallocate
等函数的正确实现,以确保与智能指针等标准库组件的正确协作。