Domanda

I am currently working on implementing something similar to this (http://molecularmusings.wordpress.com/2011/06/27/config-values/) while using the template system, so that i do not have to create a class for all types i want to support. Now the class itself works fine, but i am having trouble figuring out how to manage memory if i read config values from a file and save them to the list.

This is my ConfigSetting class:

#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;

And i am using it like this (from another class called ConfigReader):

ConfigSetting<std::string>* cf = new ConfigSetting<std::string>(key, synopsis, value);

Now my question is: What is the best way manage memory in this case? Since the list is static i cannot just run through the list deleting everything once the destructor gets called. I could be using shared_ptr like this:

shared_ptr<ConfigSetting<std::string>> sp(new ConfigSetting<std::string>(key, synopsis, value));

or another type of smart pointer? Maybe there even is more elegant solution i didn't think of.

È stato utile?

Soluzione

As far as I can see, there is nothing in your implicit destructor that must be called to ensure proper operation. If that is true, you can just forget about cleaning up your lists. Trying to do so will only increase the runtime of your program with absolutely no benefit. Just let the kernel do its job, it won't leak any memory pages just because you couldn't be bothered to clean up static data.


However, if you have a nontrivial destructor down the line somewhere which includes such important operations like flushing files or sending messages to other processes, then you must use a destructor function. I'm not talking about the normal C++ destructors here, but about a specially declared function that is executed by the runtime after main() exits.

With gcc, you declare a destructor function like this:

void foo() __attribute__((destructor));

void foo() {
    //Do vitally important cleanup here.
}

Since the linker takes care of instructing the runtime to call your destructor function, you do not have to have any call to these functions, they may actually be declared with file local visibility.


Now, you ask "Am I not supposed to delete this pointer somewhere?" Yes, you are supposed to delete it. You are supposed to call delete on every object that you create with new for two reasons:

  1. To give back the memory held by the object to the runtime, so that your process can reuse the memory for other purposes. If you fail to delete objects that you create on a regular basis, the memory footprint of your process will increase indefinitely until the kernel steps in and shoots down your process.

  2. To run the destructor for your object, which frequently results in calling delete on other objects which are not needed anymore. In most cases, this will just give back more memory according to 1., which seems to be your case. It may do more vital operations, though.

Since the objects in question have to live until the very end of your process lifetime (they are static data, after all), you cannot possibly reuse their memory. The kernel, however, is above the level of the runtime that provides you with the new and delete keywords. The kernel is the creator of your tiny process world, in which the new and delete keywords happen to live. The kernel does not care about which parts of the virtual address space your runtime considers used/unused. The kernel will simply strip down the entire virtual address space when your process exits, and the used/unused state of your memory will dissipate into nothingness.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top