面试题答案
一键面试智能指针与类型安全性的关联
- 对象生命周期管理
- std::unique_ptr:
- 它采用独占所有权模型。当
std::unique_ptr
离开其作用域时,它所指向的对象会被自动销毁。例如:
- 它采用独占所有权模型。当
- std::unique_ptr:
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
// 当ptr离开作用域,MyClass对象会被自动销毁
return 0;
}
- 这种自动销毁机制确保了对象生命周期的正确管理,避免了手动释放内存可能导致的内存泄漏,从内存管理角度保证了类型安全。
- std::shared_ptr:
- 它采用引用计数模型。多个
std::shared_ptr
可以指向同一个对象,每当一个std::shared_ptr
被创建或赋值时,引用计数增加;当std::shared_ptr
离开作用域或被重置时,引用计数减少。当引用计数变为0时,对象被自动销毁。例如:
- 它采用引用计数模型。多个
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ptr2 = ptr1;
// 此时引用计数为2
{
std::shared_ptr<MyClass> ptr3 = ptr1;
// 引用计数变为3
}
// ptr3离开作用域,引用计数变回2
// 当ptr1和ptr2离开作用域,引用计数变为0,MyClass对象被销毁
return 0;
}
- 这种机制同样保证了对象生命周期的正确管理,减少了手动管理内存的错误,提升了类型安全。
2. 避免悬空指针
- std::unique_ptr和std::shared_ptr:
- 由于它们会自动管理对象的销毁,当对象被销毁时,指向该对象的智能指针会被正确更新(
std::unique_ptr
变为空指针,std::shared_ptr
引用计数减少,当变为0时对象销毁)。不会出现普通指针在对象被手动释放后仍然指向已释放内存的悬空指针情况。例如:
- 由于它们会自动管理对象的销毁,当对象被销毁时,指向该对象的智能指针会被正确更新(
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
MyClass* rawPtr = ptr.get();
ptr.reset();
// rawPtr此时成为悬空指针,但ptr是安全的,不会导致野指针访问
return 0;
}
- std::weak_ptr:
- 它是一种弱引用,不增加对象的引用计数。主要用于解决
std::shared_ptr
的循环引用问题。它可以指向由std::shared_ptr
管理的对象,但当std::shared_ptr
的引用计数变为0,对象被销毁时,std::weak_ptr
会自动变为空指针。例如:
- 它是一种弱引用,不增加对象的引用计数。主要用于解决
#include <memory>
class B;
class A {
public:
std::weak_ptr<B> bPtr;
~A() { std::cout << "A destructor" << std::endl; }
};
class B {
public:
std::weak_ptr<A> aPtr;
~B() { std::cout << "B destructor" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->bPtr = b;
b->aPtr = a;
// 这里没有循环引用问题
// 当a和b离开作用域,A和B对象会被正确销毁,aPtr和bPtr会变为空指针
return 0;
}
- 复杂继承体系下保证类型安全
- 向上转型:
- 智能指针支持向上转型,就像普通指针一样。例如,假设有一个继承体系
class Derived : public Base
,可以将std::unique_ptr<Derived>
或std::shared_ptr<Derived>
转换为std::unique_ptr<Base>
或std::shared_ptr<Base>
。例如:
- 智能指针支持向上转型,就像普通指针一样。例如,假设有一个继承体系
- 向上转型:
#include <memory>
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
};
int main() {
std::unique_ptr<Derived> derivedPtr = std::make_unique<Derived>();
std::unique_ptr<Base> basePtr = std::move(derivedPtr);
// 类型安全的向上转型
return 0;
}
- 向下转型:
- 使用
std::dynamic_pointer_cast
进行安全的向下转型。它与dynamic_cast
类似,但是用于智能指针。如果转型成功,会返回一个有效的智能指针;如果失败,会返回一个空指针。例如:
- 使用
#include <memory>
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
};
int main() {
std::shared_ptr<Base> basePtr = std::make_shared<Derived>();
std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
if (derivedPtr) {
// 转型成功
} else {
// 转型失败
}
return 0;
}
- 这样在复杂继承体系中,智能指针通过这些机制保证了在对象转换过程中的类型安全,避免了因错误的指针类型转换导致的未定义行为。