Отключить конструктор копирования

173

У меня есть класс:

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

Как я должен изменить его, чтобы отключить код вроде:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

и разрешить только такой код:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Скромный отладчик
источник
1
Кстати, это единственное с положениями о наследовании (учитывая, защищены)?
Р. Мартиньо Фернандес
Я сомневаюсь, что в вашем коде будет каждый раз создаваться другой экземпляр, я думаю, GetUniqueInstance () всегда будет давать ссылку на один и тот же объект.
Пратам Шах

Ответы:

286

Вы можете сделать конструктор копирования закрытым и не предоставлять реализацию:

private:
    SymbolIndexer(const SymbolIndexer&);

Или в C ++ 11, явно запретите это:

SymbolIndexer(const SymbolIndexer&) = delete;
Р. Мартиньо Фернандес
источник
43
Что касается deleteключевого слова, я хотел бы добавить следующее. Моя текущая привычка при разработке нового класса заключается в том, чтобы сразу deleteи конструктор копирования, и оператор присваивания. Я обнаружил, что, в зависимости от контекста, они по большей части не нужны, и их удаление предотвращает некоторые случаи неожиданного поведения. Если возникает ситуация, когда может потребоваться копия ctor, определите, можно ли это сделать с помощью семантики перемещения. Если это нежелательно, предоставьте реализацию для (!) Оператора копирования и оператора присваивания. Будь это хороший подход, я оставлю на усмотрение читателя.
pauluss86
1
@ pauluss86 Мне нравится ваш подход, но я не буду полностью придерживаться его, так как считаю, что время, потраченное на следование этому шаблону, больше, чем время, сэкономленное на ошибках, которые он предотвращает. Я просто запрещаю копировать, когда не уверен.
Томаш Зато - Восстановить Монику
@ pauluss86 Это в основном то, что делает Rust: Move-по-умолчанию (и const-по-умолчанию). Очень полезно на мой взгляд.
Капичу
33

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

class NonAssignable {
private:
    NonAssignable(NonAssignable const&);
    NonAssignable& operator=(NonAssignable const&);
public:
    NonAssignable() {}
};

class SymbolIndexer: public Indexer, public NonAssignable {
};

Для GCC это дает следующее сообщение об ошибке:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

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

UPD:

В C ++ 11 вы также можете написать NonAssignableкласс следующим образом:

class NonAssignable {
public:
    NonAssignable(NonAssignable const&) = delete;
    NonAssignable& operator=(NonAssignable const&) = delete;
    NonAssignable() {}
};

В deleteПредотвращает ключевых слов членов от того по умолчанию возведенных, поэтому они не могут быть использованы в дальнейшем , по умолчанию , построенных членов Производный класса. Попытка присвоения дает следующую ошибку в GCC:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost уже имеет класс только для той же цели, я думаю, что он реализован аналогичным образом. Класс вызывается boost::noncopyableи предназначен для использования следующим образом:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

Я бы рекомендовал придерживаться решения Boost, если ваша политика проекта позволяет это. Смотрите также другой boost::noncopyableсвязанный вопрос для получения дополнительной информации.

firegurafiku
источник
Разве это не должно быть NonAssignable(const NonAssignable &other);?
Тройсеф
Я думаю, что этот вопрос получил бы гораздо больше голосов, если бы он был обновлен до deleteсинтаксиса ключевых слов C ++ 11 .
Томаш Зато - Восстановить Монику
@ TomášZato: Идея состоит в том, чтобы сохранить конструктор копирования и оператор присваивания присутствующим, но частным. Если вы deleteих, он перестает работать (я только что проверил).
firegurafiku
@ TomášZato: Ах, извините, мой метод тестирования был немного неправильным. Удаление тоже работает. Обновлю ответ через минуту.
firegurafiku
3
@Troyseph: const Class&и Class const&довольно то же самое. Для указателей у вас может быть даже Class const * constтип.
firegurafiku
4

Сделай SymbolIndexer( const SymbolIndexer& )приватным. Если вы назначаете ссылку, вы не копируете.

Аарон Клоц
источник