单例:是否存在内存泄漏?
Singleton: is there a memory leak?
这是一个简单的单例:
class Singleton
{
Singleton();
virtual ~Singleton();
Singleton * Singleton::getInstance()
{
static Singleton * instance;
if (!instance) {
instance = new Singleton();
};
return instance;
};
}
主代码第一次调用Singleton::getInstance()->someMethod()
时,class不是实例化了两次吗?会不会有内存泄漏?
我问是因为 Visual Leak Detector 检测到 new Singleton()
线路上的内存泄漏。
When main code calls Singleton::getInstance()->someMethod()
for the first time, isn't the class instantiated twice?
没有。调用 Singleton
的静态成员不会实例化 Singleton
,因此此处存在的 Singleton
的唯一实例是您使用 new
创建的实例。而且你只创建它一次,因为一旦 instance
指向它,你就再也不会调用 new
。
但是,您在这里遇到的一个问题是未能使 getInstance
成为静态成员函数。我假设这是一个 typo/oversight ,否则你的程序甚至无法编译。事实上,即使作为非静态成员,声明也是格式错误的。此外,构造函数应该是 private
以强制执行只有 getInstance
可以实例化类型的概念。
Will be there memory leak?
是的,这就是 Leak Detector 报告的内容。但是,它是最小的:问题是 delete
在程序关闭之前那个单例实例没有任何内容。
坦率地说,我不担心。这 可能 是可以接受泄漏的罕见情况之一,主要是因为 "leak" 它只是在进程关闭时取消分配的一次性失败。
但是,如果您完全摆脱指针,那么您可以同时避免这两个问题,因为您的程序最后要做的事情之一就是销毁静态存储持续时间的对象:
#include <iostream>
class Singleton
{
public:
~Singleton() { std::cout << "destruction!\n"; }
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
void foo() { std::cout << "foo!\n"; }
private:
Singleton() { std::cout << "construction!\n"; }
};
int main()
{
Singleton::getInstance().foo();
}
// Output:
// construction!
// foo!
// destruction!
(live demo)
甚至不需要指针!
这有一个额外的好处,即整个函数本质上是线程安全的,至少从 C++11 开始是这样。
new Singleton()
没有匹配的 delete
,所以是的,您正在泄漏资源。
你会在程序关闭时取回内存,但程序结束时不会返回所有资源。
您可以通过将实例设为 std::auto_ptr
或 std::unique_ptr
来修复它。或者干脆不使用指针。
@LightnessRacesInOrbit 回答正确而且切题。由于您的问题在某种程度上致力于实现单例时的最佳实践(顺便说一句,您应该尽量避免这种情况),所以我会告诉您我的看法。您可以使用名为 Curiously Recurring Template Pattern to create a Singleton
base class which takes as a template parameter the class you want to make a Singleton. Once you've done that correctly, creating Singletons is like a walk in the park. Just derive Foo
from Singleton<Foo>
and make Foo::Foo()
and Foo::~Foo()
private. Here is the code I use, with comments, live on Coliru:
的东西
// Singleton pattern via CRTP (curiously recurring template pattern)
// thread safe in C++11 and later
#include <iostream>
#include <type_traits>
// generic Singleton via CRTP
template <typename T>
class Singleton
{
protected:
Singleton(const Singleton&) = delete; // to prevent CASE 3
Singleton& operator=(const Singleton&) = delete; // to prevent CASE 4
Singleton() noexcept = default; // to allow creation of Singleton<Foo>
// by the derived class Foo, since otherwise the (deleted)
// copy constructor prevents the compiler from generating
// a default constructor;
// declared protected to prevent CASE 5
public:
static T& get_instance()
noexcept(std::is_nothrow_constructible<T>::value)
{
static T instance;
return instance;
}
// thread local instance
static thread_local T& get_thread_local_instance()
noexcept(std::is_nothrow_constructible<T>::value)
{
static T instance;
return instance;
}
};
// specific Singleton instance
// use const if you want a const instance returned
// make the constructor and destructor private
class Foo: public Singleton</*const*/ Foo>
{
// so Singleton<Foo> can access the constructor and destructor of Foo
friend class Singleton<Foo>;
Foo() // to prevent CASE 1
{
std::cout << "Foo::Foo() private constructor" << std::endl;
}
// OK to be private, since Singleton<Foo> is a friend and can invoke it
~Foo() // to prevent CASE 2
{
std::cout << "Foo::~Foo() private destructor" << std::endl;
}
public:
void say_hello()
{
std::cout << "\t Hello from Singleton" << std::endl;
}
};
int main()
{
Foo& sFoo = Foo::get_instance();
sFoo.say_hello();
Foo& sAnotherFoo = Foo::get_instance(); // OK, get the same instance
sAnotherFoo.say_hello();
Foo& sYetAnotherFoo = sFoo; // still OK, get the same instance
sYetAnotherFoo.say_hello();
thread_local Foo& stlFoo = Foo::get_thread_local_instance(); // thread local instance
stlFoo.say_hello();
// CASE 1: error: 'Foo::Foo()' is private
// Foo foo;
// Foo* psFoo = new Foo;
// CASE 2: error: 'Foo::~Foo()' is private
Foo* psFoo = &Foo::get_instance(); // can get pointer
psFoo->say_hello();
// delete psFoo; // cannot delete it
// CASE 3: error: use of deleted function 'Foo::Foo(const Foo&)'
// Foo foo(sFoo);
// CASE 4: error: use of deleted function 'Foo& Foo::operator=(const Foo&)'
// sFoo = sAnotherFoo;
// CASE 5: error: 'Singleton<T>::Singleton() [with T = Foo]' is protected
// Singleton<Foo> direct_sFoo;
}
这是一个简单的单例:
class Singleton
{
Singleton();
virtual ~Singleton();
Singleton * Singleton::getInstance()
{
static Singleton * instance;
if (!instance) {
instance = new Singleton();
};
return instance;
};
}
主代码第一次调用Singleton::getInstance()->someMethod()
时,class不是实例化了两次吗?会不会有内存泄漏?
我问是因为 Visual Leak Detector 检测到 new Singleton()
线路上的内存泄漏。
When main code calls
Singleton::getInstance()->someMethod()
for the first time, isn't the class instantiated twice?
没有。调用 Singleton
的静态成员不会实例化 Singleton
,因此此处存在的 Singleton
的唯一实例是您使用 new
创建的实例。而且你只创建它一次,因为一旦 instance
指向它,你就再也不会调用 new
。
但是,您在这里遇到的一个问题是未能使 getInstance
成为静态成员函数。我假设这是一个 typo/oversight ,否则你的程序甚至无法编译。事实上,即使作为非静态成员,声明也是格式错误的。此外,构造函数应该是 private
以强制执行只有 getInstance
可以实例化类型的概念。
Will be there memory leak?
是的,这就是 Leak Detector 报告的内容。但是,它是最小的:问题是 delete
在程序关闭之前那个单例实例没有任何内容。
坦率地说,我不担心。这 可能 是可以接受泄漏的罕见情况之一,主要是因为 "leak" 它只是在进程关闭时取消分配的一次性失败。
但是,如果您完全摆脱指针,那么您可以同时避免这两个问题,因为您的程序最后要做的事情之一就是销毁静态存储持续时间的对象:
#include <iostream>
class Singleton
{
public:
~Singleton() { std::cout << "destruction!\n"; }
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
void foo() { std::cout << "foo!\n"; }
private:
Singleton() { std::cout << "construction!\n"; }
};
int main()
{
Singleton::getInstance().foo();
}
// Output:
// construction!
// foo!
// destruction!
(live demo)
甚至不需要指针!
这有一个额外的好处,即整个函数本质上是线程安全的,至少从 C++11 开始是这样。
new Singleton()
没有匹配的 delete
,所以是的,您正在泄漏资源。
你会在程序关闭时取回内存,但程序结束时不会返回所有资源。
您可以通过将实例设为 std::auto_ptr
或 std::unique_ptr
来修复它。或者干脆不使用指针。
@LightnessRacesInOrbit 回答正确而且切题。由于您的问题在某种程度上致力于实现单例时的最佳实践(顺便说一句,您应该尽量避免这种情况),所以我会告诉您我的看法。您可以使用名为 Curiously Recurring Template Pattern to create a Singleton
base class which takes as a template parameter the class you want to make a Singleton. Once you've done that correctly, creating Singletons is like a walk in the park. Just derive Foo
from Singleton<Foo>
and make Foo::Foo()
and Foo::~Foo()
private. Here is the code I use, with comments, live on Coliru:
// Singleton pattern via CRTP (curiously recurring template pattern)
// thread safe in C++11 and later
#include <iostream>
#include <type_traits>
// generic Singleton via CRTP
template <typename T>
class Singleton
{
protected:
Singleton(const Singleton&) = delete; // to prevent CASE 3
Singleton& operator=(const Singleton&) = delete; // to prevent CASE 4
Singleton() noexcept = default; // to allow creation of Singleton<Foo>
// by the derived class Foo, since otherwise the (deleted)
// copy constructor prevents the compiler from generating
// a default constructor;
// declared protected to prevent CASE 5
public:
static T& get_instance()
noexcept(std::is_nothrow_constructible<T>::value)
{
static T instance;
return instance;
}
// thread local instance
static thread_local T& get_thread_local_instance()
noexcept(std::is_nothrow_constructible<T>::value)
{
static T instance;
return instance;
}
};
// specific Singleton instance
// use const if you want a const instance returned
// make the constructor and destructor private
class Foo: public Singleton</*const*/ Foo>
{
// so Singleton<Foo> can access the constructor and destructor of Foo
friend class Singleton<Foo>;
Foo() // to prevent CASE 1
{
std::cout << "Foo::Foo() private constructor" << std::endl;
}
// OK to be private, since Singleton<Foo> is a friend and can invoke it
~Foo() // to prevent CASE 2
{
std::cout << "Foo::~Foo() private destructor" << std::endl;
}
public:
void say_hello()
{
std::cout << "\t Hello from Singleton" << std::endl;
}
};
int main()
{
Foo& sFoo = Foo::get_instance();
sFoo.say_hello();
Foo& sAnotherFoo = Foo::get_instance(); // OK, get the same instance
sAnotherFoo.say_hello();
Foo& sYetAnotherFoo = sFoo; // still OK, get the same instance
sYetAnotherFoo.say_hello();
thread_local Foo& stlFoo = Foo::get_thread_local_instance(); // thread local instance
stlFoo.say_hello();
// CASE 1: error: 'Foo::Foo()' is private
// Foo foo;
// Foo* psFoo = new Foo;
// CASE 2: error: 'Foo::~Foo()' is private
Foo* psFoo = &Foo::get_instance(); // can get pointer
psFoo->say_hello();
// delete psFoo; // cannot delete it
// CASE 3: error: use of deleted function 'Foo::Foo(const Foo&)'
// Foo foo(sFoo);
// CASE 4: error: use of deleted function 'Foo& Foo::operator=(const Foo&)'
// sFoo = sAnotherFoo;
// CASE 5: error: 'Singleton<T>::Singleton() [with T = Foo]' is protected
// Singleton<Foo> direct_sFoo;
}