Работа с постоянными и непостоянными функциями - C ++ - PullRequest
3 голосов
/ 14 февраля 2010

у меня 3 класса. В простейшем виде это выглядит так:

class tree
{
public:
    tree_node* find_node(const std::string& text) {
       return factory.find(text);
    }
private:
    tree_node_factory factory;
}

class tree_node
{
public:
    tree_node(const std::string& text) : text_(text) {}

    const std::string& text() const {
       return text_;
    }

    void set_parent(const tree_node* new_parent);

private:
    std::string text_;
}

class tree_node_factory
{
public:
    tree_node* find(const std::string& text);
private:
    std::vector<tree_node*> allocated_nodes;
}

Я не хочу, чтобы пользователи tree могли изменять tree_node, возвращаемый такими методами, как find_node. Поэтому я изменил find_node и tree_node_factory::find на

const tree_node* find_node(const std::string& text) const {
    return factory.find(text);
}

const tree_node* find(const std::string& text) const;

Проблема в том, что tree внутренне должен иметь возможность изменять узлы и работать с такими методами, как set_parent. Но поскольку фабрика возвращает только const узлов, я добавил еще одну перегрузку (неконстантную версию) find в фабрику.

tree_node* find(const std::string& text);

Мне интересно, это правильный способ решения подобных проблем? Я вижу, что код дублируется в константной и неконстантной версиях.

Есть мысли ..?

Ответы [ 2 ]

7 голосов
/ 14 февраля 2010

Пункт 3 в книге Скотта Мейерса Эффективный C ++ демонстрирует метод удаления этого дублирования кода. По сути, в вашей неконстантной функции вы добавите const к this, вызовите версию const, а затем отбросите const. Это безопасно; хотя запись в const-переменную приводит к неопределенному поведению, потому что this изначально был неконстантным, все в порядке.

Пример:

const std::string& operator[](size_t index) const
{
    // some other code

    // since `this` isn't really const, this is modifiable 
    return mData[index]; 
}

std::string& operator[](size_t index)
{
    return const_cast<std::string&> // (3) take const off result
            (static_cast<const my_type&> // (1) add const
            (*this)[index]); // (2) use const version

}

Обычно все это будет в одной строке. Вы также можете сделать утилиту для него.

Обратите внимание на предостережение: если константная версия возвращает «реальный» константный объект, этот метод явно приводит к неопределенному поведению. Константность возвращаемого значения должна быть отражена константой объекта, на который указывает this. Это неработающий код:

const std::string& operator[](size_t index) const
{
    static const std::string constString = "Don't modify me.";

    if (index == 0)
    {
        // even though `this` isn't really const, this is NOT modifiable
        return constString; 
    }

    return mData[index - 1];
}

std::string& operator[](size_t index)
{
    return const_cast<std::string&> // (3) !!! take const off result !!!
            (static_cast<const my_type&> // (1)
            (*this)[index]); // (2)

}

На практике мы избегаем глобального состояния, поэтому это редко является проблемой. В любом случае, это тривиально проверить.

3 голосов
/ 14 февраля 2010

К сожалению, в C ++ нет инструментов (кроме макросов) для устранения дублирования исходного кода при перегрузках функций, которые выглядят в основном одинаково, но отличаются по константности. Однако вы можете реализовать одну из функций, используя другую и const_cast.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...