引言
在现代软件开发中,内存管理是一个核心主题,特别是在使用C++这类需要手动管理内存的语言时。智能指针作为一种高效的工具,能够简化内存管理的复杂性。本文将讨论如何利用 std::unique_ptr 来封装复杂的内存管理任务,特别是在涉及栈上对象和多态性时的情况。通过使用定制删除器(custom deleter),我们可以灵活地控制内存的释放行为,从而适应更多复杂的使用场景。
正文
深入复制和多态克隆
假设我们要实现一个图形编辑软件,同一种图形在不同的构建场所有不同的效果,所以,我们一般会在图形类中维护一个构建指令:
class Shape {
public:explicit Shape(std::unique_ptr<Instructions> instructions): instructions_(std::move(instructions)) {}Shape(const Shape& other): instructions_(other.instructions_->clone()) {}
private:std::unique_ptr<Instructions> instructions_;
};
构建指令主要分为基础图形(BasicShape
)和UML图形(UMLShape),另外,Instructions
类具有多态克隆功能:
class Instructions {
public:virtual std::unique_ptr<Instructions> clone() const = 0;virtual ~Instructions() {}
};class BasicShape : public Instructions {
public:std::unique_ptr<Instructions> clone() const { return std::unique_ptr<Instructions>(new BasicShape(*this)); }
};class UMLShape : public Instructions {
public:std::unique_ptr<Instructions> clone() const { return std::unique_ptr<Instructions>(new UMLShape(*this)); }
};
构建一个Shape
的方法如下:
enum class DrawingMethod {basicShape,umlShape
};Shape buildAShape(DrawingMethod method) {if (method == DrawingMethod::basicShape)return Shape(std::unique_ptr<Instructions>(new BasicShape));if (method == DrawingMethod::umlShape)return Shape(std::unique_ptr<Instructions>(new UMLShape));throw InvalidDrawingMethod();
}
正常的使用场景就是通过buildAShape
方法创建Shape
对象,但是在实际开发过程中,也可能会有其他方式去实现。比如,通过栈上的指令对象来创建临时的Shape
对象:
UMLShape umlShape;
Shape shape(???); // 如何将 umlShape 传递给 shape?
这种情况下我们不能直接把unique_ptr
绑定到栈分配的对象,因为栈对象在跳出作用域后会自动销毁,而使用unique_ptr
会在指针对象跳出作用域时触发delete
会导致未定义行为。
深入std::unique_ptr
std::unique_ptr
是一个模板类,而且它还不止一个模板参数:
template<typename T,typename Deleter = std::default_delete<T>
> class unique_ptr;
std::default_delete<T>
是一个调用delete
的函数对象,它可以用自定义删除器替换。
解决方案
上面的问题是我们希望unique_ptr
能够销毁Instructions
,如果Instructions
来自栈内存,那么我们就希望unique_ptr
不会去销毁对象。
那么我们就通过多种删除器来解决这个问题:
删除器代码:
void deleteInstructions(const Instructions* instructions) { delete instructions; }
void doNotDeleteInstructions(const Instructions* instructions) {}
如何使用这些删除器呢:
using InstructionsUniquePtr = std::unique_ptr<const Instructions, void(*)(const Instructions*)>;enum class DrawingMethod {basicShape,umlShape
};Shape buildAShape(DrawingMethod method) {if (method == DrawingMethod::basicShape)return Shape(InstructionsUniquePtr(new BasicShape, deleteInstructions));if (method == DrawingMethod::umlShape)return Shape(InstructionsUniquePtr(new UMLShape, deleteInstructions));throw InvalidDrawingMethod();
}
如果指令对象来自栈:
UMLShape umlShape;
Shape shape(InstructionsUniquePtr(¨Shape, doNotDeleteInstructions));