Невозможно назначить нестатический член данных в константной функции-члене

Я пытаюсь использовать std::unordered_set в качестве хеш-таблицы для хранения множества CreditCard. CreditCard и другой класс CardDatabase определяются следующим образом:

class CreditCard {
private:
    string cardHolder;
    unsigned long long cardNumber;
    int limit;
    int balance;

public:
    CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
        cardHolder = in_cardHolder;
        cardNumber = stoll(in_cardNumber);
        limit = in_limit;
        balance = 0;
    }

    void ChangeBalance(int amount) const {
        balance += amount; // SECOND ERROR
    }
};

class CardDatabase {
private:
    unordered_set<CreditCard> cards;
    unordered_set<CreditCard>::iterator iter;

public:
    CardDatabase() { }
    
    void AddCard(cardHolder, cardNumber, int limit) {
        CreditCard tempCard = CreditCard(cardHolder, cardNumber, limit);
        cards.insert(tempCard);
    }

    void Charge(string cardHolder, int chargeAmount) {
        iter = cards.find(cardHolder);
        iter->ChangeBalance(chargeAmount); // FIRST ERROR
    }
}

Первоначально я получал следующую ошибку компиляции в FIRST ERROR: Member function 'ChangeBalance' not viable: 'this' argument has type 'const CreditCard', but function is not marked const. Итак, я добавил константу в функцию ChangeBalance. Однако после этого я получаю следующую ошибку компиляции в SECOND ERROR: Cannot assign to non-static member within const member function 'ChangeBalance'.

Есть ли способ исправить эту ошибку, не меняя balance на статическую переменную? Очевидно, важно, чтобы баланс был разным для каждого экземпляра CreditCard.

Любая помощь приветствуется.

РЕДАКТИРОВАТЬ:

Спасибо всем за ваши быстрые ответы. Я чувствую, что должен кое-что прояснить. Я уже добавил правильную функциональность хеширования в другом месте моего кода:

namespace std {
    template <>
    struct hash<CreditCard> {
        size_t operator()(const CreditCard& cc) const
        {
            return hash<string>()(cc.GetCardHolder());
        }
    }
}

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


person bpgeck    schedule 06.10.2016    source источник
comment
Что вы ожидаете от CreditCard::CreditCard и CreditCard::ChangeBalance?   -  person tkausl    schedule 06.10.2016


Ответы (4)


Члены unordered_set постоянны и не могут быть изменены, когда они находятся в unordered_set по умолчанию. Вы пытаетесь изменить объекты в наборе, а компилятор правильно говорит вам, что вы не можете этого сделать.

Единственный возможный способ сделать это правильно (объяснено только в образовательных целях, потому что это плохой дизайн класса):

  1. Явно объявите отдельные поля, которые можно изменить таким образом, как mutable.

  2. Используйте пользовательскую хеш-функцию с вашим unordered_set, и хэш-функция должна исключать значение изменяемых полей из значения вычисляемого хэша.

В противном случае изменение содержимого объекта в наборе, очевидно, изменит его хеш-значение, что приведет к неопределенному поведению.

Опять же, это объясняется только в информационных целях. Это не лучший дизайн класса.

Чистым способом сделать это было бы назначить уникальный идентификатор для каждого CreditCard (вы знаете, как номер кредитной карты?) и использовать обычный std::map для поиска CreditCard по их номеру.

person Sam Varshavchik    schedule 06.10.2016
comment
Спасибо за ваш ответ и ваше полезное объяснение - person bpgeck; 06.10.2016

Для ChangeBalance не подходит семантика const. По самой природе его имени вы изменяете объект. Сделать функцию неконстантной.

void ChangeBalance(int amount) {
    balance += amount;
}

Другая проблема заключается в том, что вы неправильно вызвали свою функцию. Вместо этого вы должны сделать это:

iter->ChangeBalance(chargeAmount);

Я упомяну, что бывают случаи, когда вы хотите изменить значения в объекте const, и для этого есть модификатор типа mutable. Однако не используйте его для устранения текущей ошибки!

person paddy    schedule 06.10.2016
comment
Спасибо, что так быстро ответили. Причина для CreditCard:: заключалась в том, что я копировал только те части, в которых у меня были ошибки из гораздо большей базы кода. Я отредактировал свой вопрос соответственно - person bpgeck; 06.10.2016
comment
iter->CreditCard::ChangeBalance(chargeAmount); является правильным, CreditCard:: в этом случае является избыточным (но могут быть и другие иерархии классов, в которые вы можете включить это). - person M.M; 06.10.2016

void ChangeBalance(int amount) не должно быть const - это изменение объекта.

Проблема в итераторе раньше: cards.find возвращает объект const, поэтому вам не разрешено его изменять.

Способ решить эту проблему состоит в том, чтобы заставить ваш cards установить набор указателей на карты, а не на карты; или использовать другой способ найти совпадающую карту

person Aganju    schedule 06.10.2016

Играя быстро и свободно с синтаксисом C++ в этом коде tar, Хосс. Множество ошибок ждут за углом

Первая ошибка:

iter->CreditCard::ChangeBalance(chargeAmount);

должно быть

iter->ChangeBalance(chargeAmount);

Откровенно плохой синтаксис, который, вероятно, возникает из-за ошибок, возникающих из-за того, что unordered_set не знает, как хэшировать CreditCard. Прочтите это: Как мне использовать unordered_set? Тем не менее, unordered_set вероятно, не правильное решение для этой работы. std::map<std::string, CreditCard> выглядит более точным.

Использование неправильного решения для устранения вышеуказанной проблемы приводит к

Вторая ошибка:

void ChangeBalance(int amount) const

const в методе означает, что метод не может изменить состояние объекта. in ChangeBalance balance += amount; пытается изменить состояние объекта, обновив переменную-член.

Кроме того, компилятор возненавидит CreditCard:: в этом:

CreditCard::CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
    cardHolder = in_cardHolder;
    cardNumber = stoll(in_cardNumber);
    limit = in_limit;
    balance = 0;
}
person user4581301    schedule 06.10.2016