C++模板类Indestructible
,它被设计用于实现一种特殊的单例模式——Meyers单例,但在此基础上,它确保了对象在程序整个生命周期内都不会被销毁,并且对象被内联存储(即存储在栈上,而非堆上)。这种设计旨在避免在对象构造时分配内存以及在访问时解引用指针,从而提高了效率。
在Meyers单例模式中,通常的做法是在函数内部声明一个static
局部变量,这个变量在函数第一次被调用时构造,并且在程序结束时销毁(除非它是const
的,且其类型拥有静态存储期,这种情况下它实际上不会在程序结束时销毁,但其析构函数仍然会在程序结束时被调用,只是不会释放内存)。然而,对于某些关键资源或配置数据,我们可能希望它们在程序整个运行期间都保持有效,且不希望它们的析构函数被调用,因为析构操作可能涉及释放资源、写入文件或执行其他清理工作,这些在程序正常退出时可能是不必要的或有害的。
Indestructible
模板类通过以下方式实现这一目标:
- 它接受一个类型作为模板参数,并实例化该类型的对象。
- 它确保这个对象在构造后不会被销毁。这通常是通过将对象声明为
static const
来实现的,因为const
全局或静态对象在程序结束时不会被销毁(尽管它们的析构函数会被调用,但析构函数体可以是空的或不执行任何重要操作)。 - 它通过将对象内联存储在调用它的函数或作用域内,从而避免了额外的内存分配和指针解引用。
然而,需要注意的是,Indestructible
类(或任何类似的设计)应该谨慎使用,因为它违反了常规的C++资源管理原则。特别是,它使得对象的生命周期与程序的运行期完全绑定,这可能导致资源泄露(如果对象持有需要在程序结束前释放的资源)或其他难以调试的问题。此外,由于对象不会被销毁,因此任何依赖于对象析构函数进行清理的代码都将无法正常工作。
在您的示例中,Indestructible<map<string, int>>
被用于存储一个键值对映射,这个映射在doSomethingWithExpensiveData
函数第一次被调用时构造,并在整个程序运行期间保持有效。然而,需要注意的是,虽然map
对象的析构函数在程序结束时会被调用(如果它不是noexcept
的,这甚至可能导致程序终止),但实际上它不会释放任何内存(因为static const
对象通常分配在只读数据段)。此外,如果map
中存储了指向动态分配内存的指针或其他需要显式清理的资源,则这些资源将不会被自动释放。
因此,在使用Indestructible
或类似的设计时,请务必确保您完全理解其工作原理和潜在的风险。
源码为:
template <typename T>
class Indestructible final {//不允许继承public:template <typename S = T, typename = decltype(S())>constexpr Indestructible() noexcept(noexcept(T())) {}template <typename U = T,_t<std::enable_if<std::is_constructible<T, U&&>::value>>* = nullptr,_t<std::enable_if<!std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>>* =nullptr,_t<std::enable_if<!std::is_convertible<U&&, T>::value>>* = nullptr>explicit constexpr Indestructible(U&& u) noexcept(noexcept(T(std::declval<U>()))): storage_(std::forward<U>(u)) {}template <typename U = T,_t<std::enable_if<std::is_constructible<T, U&&>::value>>* = nullptr,_t<std::enable_if<!std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>>* =nullptr,_t<std::enable_if<std::is_convertible<U&&, T>::value>>* = nullptr>/* implicit */ constexpr Indestructible(U&& u) noexcept(noexcept(T(std::declval<U>()))): storage_(std::forward<U>(u)) {}template <typename... Args, typename = decltype(T(std::declval<Args>()...))>explicit constexpr Indestructible(Args&&... args) noexcept(noexcept(T(std::declval<Args>()...))): storage_(std::forward<Args>(args)...) {}template <typename U,typename... Args,typename = decltype(T(std::declval<std::initializer_list<U>&>(),std::declval<Args>()...))>explicit constexpr Indestructible(std::initializer_list<U> il, Args... args) noexcept(noexcept(T(std::declval<std::initializer_list<U>&>(),std::declval<Args>()...))): storage_(il, std::forward<Args>(args)...) {}
//以上十几个默认构造函数~Indestructible() = default;Indestructible(Indestructible const&) = delete;Indestructible& operator=(Indestructible const&) = delete;Indestructible(Indestructible&& other) noexcept(noexcept(T(std::declval<T>()))): storage_(std::move(other.storage_.value)) {other.erased_ = true;}Indestructible& operator=(Indestructible&& other) noexcept(noexcept(T(std::declval<T>()))) {storage_.value = std::move(other.storage_.value);other.erased_ = true;}T* get() noexcept {check();return &storage_.value;}T const* get() const noexcept {check();return &storage_.value;}T& operator*() noexcept { return *get(); }T const& operator*() const noexcept { return *get(); }T* operator->() noexcept { return get(); }T const* operator->() const noexcept { return get(); }
//获取存储数据的一些操作private:void check() const noexcept {assert(!erased_);}union Storage {T value;template <typename S = T, typename = decltype(S())>constexpr Storage() noexcept(noexcept(T())) : value() {}template <typename... Args, typename = decltype(T(std::declval<Args>()...))>explicit constexpr Storage(Args&&... args) noexcept(noexcept(T(std::declval<Args>()...))): value(std::forward<Args>(args)...) {}~Storage() {}};Storage storage_{};//存储数据采用联合体bool erased_{false};
};
使用示例:
static const Indestructible<map<string, int>> data{map<string, int>{{"key1", 17}, {"key2", 19}, {"key3", 23}}};auto& m = *data;EXPECT_EQ(19, m.at("key2"));