我目前正致力于实现与此类似的功能 (http://molecularmusings.wordpress.com/2011/06/27/config-values/) 使用模板系统时,我不必为我想要支持的所有类型创建一个类。现在这个类本身工作正常,但是如果我从文件中读取配置值并将它们保存到列表中,我就无法弄清楚如何管理内存。
这是我的ConfigSetting类:
#pragma once
template <typename T>
class ConfigSetting {
public:
static ConfigSetting* head;
static ConfigSetting* tail;
public:
ConfigSetting(const std::string& name, const std::string& synopsis, T initValue) : m_name(name), m_synopsis(synopsis), m_value(initValue)
{
this->addToList();
}
// Special constructor for int ranges
ConfigSetting(const std::string& name, const std::string& synopsis, T initValue, T minValue, T maxValue) : m_name(name), m_synopsis(synopsis), m_value(initValue), m_min(minValue), m_max(maxValue)
{
this->addToList();
}
ConfigSetting& operator=(T value)
{
this->m_value = value;
return *this;
}
inline operator T(void) const
{
return m_value;
}
static ConfigSetting* findSetting(const std::string& name)
{
if (head) {
ConfigSetting* temp = head;
while (temp != nullptr) {
if (temp->m_name == name) {
return temp;
}
temp = temp->m_next;
}
}
return nullptr;
}
private:
void addToList(void)
{
if (head) {
tail->m_next = this;
tail = this;
}
else {
head = this;
tail = this;
}
}
ConfigSetting* m_next;
const std::string m_name;
const std::string m_synopsis;
T m_value;
T m_min;
T m_max;
};
template<class T> ConfigSetting<T>* ConfigSetting<T>::head = nullptr;
template<class T> ConfigSetting<T>* ConfigSetting<T>::tail = nullptr;
我正在使用它(来自另一个名为ConfigReader的类):
ConfigSetting<std::string>* cf = new ConfigSetting<std::string>(key, synopsis, value);
现在我的问题是:在这种情况下管理内存的最佳方法是什么?由于列表是静态的,因此一旦析构函数被调用,我就无法通过列表删除所有内容。我可以像这样使用shared_ptr
:
shared_ptr<ConfigSetting<std::string>> sp(new ConfigSetting<std::string>(key, synopsis, value));
或其他类型的智能指针?也许有更优雅的解决方案,我没有想到。
答案 0 :(得分:1)
据我所知,隐式析构函数中没有任何内容可以必须被调用以确保正常运行。如果这是真的,您可以忘记清理列表。尝试这样做只会增加程序的运行时间,绝对没有任何好处。只要让内核完成它的工作,它就不会泄漏任何内存页面,只是因为你不能打扰清理静态数据。
但是,如果你有一个非常重要的析构函数,包括诸如刷新文件或向其他进程发送消息等重要操作,那么你必须使用析构函数。我不是在谈论这里的普通C ++析构函数,而是在main()
退出之后由运行时执行的特殊声明的函数。
使用gcc
,您可以声明这样的析构函数:
void foo() __attribute__((destructor));
void foo() {
//Do vitally important cleanup here.
}
由于链接器负责指示运行时调用析构函数,因此您不必对这些函数进行任何调用,实际上可以使用文件本地可见性声明它们。
现在,你问“我不应该在某个地方delete
这个指针?”是的,你假设删除它。您应该使用delete
为使用new
创建的每个对象调用delete
,原因有两个:
将对象持有的内存返回给运行时,以便您的进程可以将内存重用于其他目的。如果您未能定期创建delete
个对象,则进程的内存占用量将无限增加,直到内核进入并关闭您的进程。
运行对象的析构函数,这经常导致在其他不再需要的对象上调用new
。在大多数情况下,这只会根据1回馈更多内存,这似乎是你的情况。不过,它可能会做更重要的操作。
由于有问题的对象必须存活到你的进程生命周期的最后(毕竟它们是静态数据),你不可能重用它们的内存。但是,内核高于为您提供delete
和new
关键字的运行时级别。内核是您的小型流程世界的创建者,其中delete
和{{1}}关键字恰好存在。内核不关心运行时认为使用/未使用的虚拟地址空间的哪些部分。当进程退出时,内核将简单地删除整个虚拟地址空间,并且内存的已使用/未使用状态将消失为虚无。