В c ++, имеет ли смысл запрещать создание копий, если конструкция по умолчанию запрещена в первую очередь? - PullRequest
2 голосов
/ 31 октября 2019

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

class CantInstantiate
{
    CantInstantiate();
    CantInstantiate(const CantInstantiate&);
    ...
};

Действительно ли необходимо сделать конструктор копирования закрытым неопределенным, если конструктор по умолчанию уже стал закрытым неопределенным (если нет другого конструктора)? В чем преимущество предотвращения копирования объекта, если у нас нет исходного объекта? Пожалуйста, объясни. Заранее спасибо.

1 Ответ

0 голосов
/ 31 октября 2019

Если целью является предотвращение создания экземпляров класса, вам необходимо убедиться, что вы не можете вызвать какой-либо конструктор. Если вы явно не запрещаете конструкцию копирования, то вы оставляете за компилятором решение решать, включать его или нет, в зависимости от условий, при которых удаляется неявно объявленным конструктором копирования , которые:

- T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
- T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors);
- T has direct or virtual base class with a deleted or inaccessible destructor; 
- T is a union-like class and has a variant member with non-trivial copy constructor;
- T has a data member of rvalue reference type;
- T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted).

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

class A
{
    A(){};
};

struct B : public A {
    B() : A(*a)
    {}
    A* a;
};

B b;
A a (*static_cast<A*>(nullptr));

Теперь по общему признанию приведенный выше код может вызвать непредвиденное поведение, и любой приличный компилятор выдаст предупреждения, если вы попытаетесь это сделать, но он скомпилируется. Практичность такого подхода будет полностью зависеть от того, что еще было объявлено в A ...

. Я бы сказал, что если идея состоит в том, чтобы остановить творение, то вам нужно убедиться, что все методы построения предотвращены. Следовательно, гораздо лучше подходить к явному удалению конструктора: default, конструктора copy и конструктора move. Это посылает гораздо более четкое заявление о намерениях, и я считаю, что это должно предотвратить все методы построения.

class CantInstantiate
{
public:
    CantInstantiate() = delete;
    CantInstantiate(const CantInstantiate&) = delete;
    CantInstantiate(CantInstantiate&&) = delete;
    ...
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...