LINUX.ORG.RU

[C++] Я правильно понимаю предназначение const_cast?

 


0

1

У меня среди полей класса есть некоторый массив.

Этот массив заполняется только в конструкторе. Больше он не меняется.

Логично бы было сделать этот массив константным. Но его заполнение делается по сложному алгоритму, так что заполнение в списке инициализации не представляется возможным, приходится заполнять в теле конструктора.

Решение — сделать его константным, а заполнение провести, воспользовавшись const_cast для снятия константности.

Так?

★★★★★

Последнее исправление: Obey-Kun (всего исправлений: 1)

Наверное, как-то так лучше сделать:

#include <QDebug>
#include <QList>

class A {
public:
  A(const QList<int> a)
  : m_a(a)
  {}

  const QList<int> getA() const {
    return m_a;
  }

private:
  const QList<int> m_a;
};

int main(int argc, char *argv[]) {
  A a(QList<int>() << 1 << 2 << 3);
  qDebug() << a.getA();
  return 0;
}

rival ★★
()

> Логично бы было сделать этот массив константным.
Кстати, зачем?

rival ★★
()

>Так?

Смысл этого действия? Засветиться на каком-нибудь говнокод.ру? Есои изменение этого массива не противоречит логике работы, ни в коем случае не надо делать его константным.

KISS

linuxfan
()
Ответ на: комментарий от linuxfan

В моём случае, видимо, противоречит. Вот чем я занимаюсь:

class RemoveBlocksCommand : public QUndoCommand
{
public:
    RemoveBlocksCommand(Scene *scene, const QList<Block *> &blocks,
                        QUndoCommand *parent = 0):
    QUndoCommand(parent),
    m_scene(scene),
    m_blocks() {
        foreach(Block *block, blocks) {
            if(block->isRemovable()) {
                m_blocks.push_back(block);
            }
        }
    }

    void redo() {
        foreach(Block *block, m_blocks) {
            m_scene->removeItem(block);
        }
    }

    void undo() {
        foreach(Block *block, m_blocks) {
            m_scene->addItem(block);
        }
    }
private:
    Scene *const m_scene;
    QList<Block *> m_blocks;
};

Класс предназначен для системы Undo/Redo в Qt и удаляет блоки.

В конструктор подаётся список блоков на удаление. Среди них могут быть как удаляемые, так и нет.

Логично тут сделать m_blocks константной или нет?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от rival

> То есть заполнить массив где-нибудь снаружи (в static методе?) и передать конструктору.

Оно может использовать какие-то только ей ведомые алгоритмы, так что то, что создаёт такой класс, понятия не должно иметь, что там должно быть в списке.

Пример с isRemovable() — упрощение. Как правило, тут всё ещё сложнее.

Obey-Kun ★★★★★
() автор топика

а не проще сделать этот массив private member и реализовать только методы его чтения?

Corey
()
Ответ на: комментарий от Obey-Kun

> Логично тут сделать m_blocks константной или нет?

Переменная m_blocks у тебя приватная, геттера нет, то есть никто кроме твоего объекта изменить ее не сможет. Зачем тебе делать ее константной?

rival ★★
()
Ответ на: комментарий от Corey

Посмотри код выше, оно так и есть. Тут вопрос в идеологии.

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

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от rival

> Переменная m_blocks у тебя приватная, геттера нет, то есть никто кроме твоего объекта изменить ее не сможет. Зачем тебе делать ее константной?

А зачем нужны константы? Чтобы случайно их не поменять.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun
class RemoveBlocksCommand : public QUndoCommand
{
public:
    static RemoveBlocksCommand* createRemoveBlocksCommand(Scene *scene, const QList<Block *> &blocks, QUndoCommand *parent = 0) {
         // обрабатывай blocks как тебе угодно
         return new RemoveBlocksCommand(scene, blocks, parent);
    }

    void redo();
    void undo();
private:
    RemoveBlocksCommand(Scene *scene, const QList<Block *> &blocks,
                        QUndoCommand *parent):
    QUndoCommand(parent),
    m_scene(scene),
    m_blocks(blocks) {}

    Scene *const m_scene;
    QList<Block *> m_blocks;
};
rival ★★
()
Ответ на: комментарий от Obey-Kun

> А зачем нужны константы? Чтобы случайно их не поменять.

Хех. Еще как вариант можно сделать приватный объект с данными, который предоставляет только константные геттеры.

rival ★★
()
Ответ на: комментарий от rival

красавчик! и почему я о таком не подумал? а ведь ещё можно так:

class RemoveBlocksCommand : public QUndoCommand
{
public:
    RemoveBlocksCommand(Scene *scene, const QList<Block *> &blocks,
                        QUndoCommand *parent = 0):
    QUndoCommand(parent),
    m_scene(scene),
    m_blocks(removableBlocks(blocks)) { }

    void redo() {
        foreach(Block *block, m_blocks) {
            m_scene->removeItem(block);
        }
    }

    void undo() {
        foreach(Block *block, m_blocks) {
            m_scene->addItem(block);
        }
    }
private:
    Scene *const m_scene;
    const QList<Block *> m_blocks;

    QList<Block *> removableBlocks(const QList<Block *> &blocks) const {
        QList<Block *> result;
            foreach(Block *block, blocks) {
                if(block->isRemovable()) {
                    result.push_back(block);
                }
            }
        return result
        }

};

Плюсы такого подхода — не меняется способ работы класса, со стороны всё выглядит логично.

Минус — работает чуть медленней по сравнению с твоим вариантом.

Так как разница в скорости незначительная, предпочту этот подход.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

кстати, (идеологически) есть ли тут смысл делать не QList<Block *> removableBlocks(const QList<Block *> &blocks) const, а static QList<Block *> removableBlocks(const QList<Block *> &blocks)?

ведь возвращаемое этим методом по сути не зависит от экземпляра класса.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от rival

Понятно. В общем, практически это не обязательно, но идеологически верно. Так и буду делать. Спасибо.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от rival

А дай чего почитать по паттернам программирования. И вообще чего-нибудь полезного. По ходу, уже достиг того уровня, когда их лучше знать. Как-никак, мой первый большой проект уже имеет в себе 7000 строк. Но до релиза примерно половина от этих 7000...

Читал только Джоссатиса и Шилдта (на первых этапах), потом Шлее (это про Qt) и документацию Qt.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

Рекомендую почитать по паттернам эту:
Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес
Приемы объектно-ориентированного проектирования. Паттерны проектирования

amtee
()
Ответ на: комментарий от Obey-Kun

А дай чего почитать по паттернам программирования. И вообще чего-нибудь полезного.

sourcemaking.com

shty ★★★★★
()

Тред не читал.

Что бы сделал я: маленький класс-обёртку для этого массива; сам массив - приватный мембер этого класса, не константный; в конструкторе класса-обётрки массив заполняется как нужно. Объект класса-обёртки делается константным мембером основного класса и конструируется в списке инициализации. Сам класс-обёртка делается приватным внутри основного класса, чтоб снаружи его никто не видел и не слышал.

Miguel ★★★★★
()
Ответ на: комментарий от Obey-Kun

я бы не делал метод статичным, вдруг у тебя потом будут разные алгоритмы и метод будет перегружен, например
так же с константой, то что ты его сделаешь константой ни от чего тебя не спасет, а потом ВДРУГ ВНЕЗАПНО все же придется его изменять, привет рефакторинг.
Идеология не в том что бы константы лепить и метод выбирать статик не статик, идеология в другом. Есть языки в которых ни констант ни приватов нет и ниче, очень даже идеологичны.

trashymichael ★★★
()
Ответ на: комментарий от trashymichael

Сколько ни думал, так и не придумал, в каких случая этот метод пришлось бы сделать не статичным, а список не константным. Сам смысл этого класса тогда теряется.

Obey-Kun ★★★★★
() автор топика

вообще const_cast считается грязным хаком. Читайте Страуструпа.

irq
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.