2012-01-30 3 views
0

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

class NotCopyable 
{ 
public: 
    NotCopyable(const double& attr1, const double& attr2) : _attr1(attr1), _attr2(attr2) {} 
    ~NotCopyable(void) {} 

private: 
    NotCopyable& operator=(const NotCopyable&); 
    NotCopyable(const NotCopyable&); 
    double _attr1; 
    double _attr2; 
}; 

Все в порядке, за исключением, когда я хотел бы присвоить массив:

NotCopyable arr[] = 
{ 
    NotCopyable(1, 0), 
    NotCopyable(2, 3) 
}; 

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

class NotCopyable 
{ 
public: 
    NotCopyable(const double& attr1, const double& attr2) : _attr1(attr1), _attr2(attr2) {} 
    ~NotCopyable(void) {} 
    NotCopyable(const NotCopyable&) 
    { 
     std::cout << "COPYING" << std:: endl; 
    } 
private: 
    NotCopyable& operator=(const NotCopyable&); 

    double _attr1; 
    double _attr2; 
}; 

Программа компилируется без ошибок, но копирование конструктор не вызывается. Поэтому возникает вопрос: как мне запретить копирование, но все же есть возможность назначать массивы?

+4

невозможно. «присвоение» массивов по-прежнему «присваивание», которое вы пытаетесь запретить – valdo

+2

Вы не можете назначить массивы. Код, показанный здесь, - это * инициализация * массива, который, даже если он использует тот же символ, не является присвоением. –

+0

Вы можете прочитать следующее: http://physical-thought.blogspot.com/2008/08/noncopyable-c-class-which-cannot-be.html esp второй комментарий, написанный автором относительно того, когда вы хотите сделать это. –

ответ

7

Ваш код arr [] = { NotCopyable(1,2) }; запрашивает конструктор копирования, по крайней мере, формально. Практически копия обычно удаляется, но подпадает под правило «как есть», а конструктор копирования все же должен быть доступен, хотя в конечном счете он не используется. (В GCC вы можете сказать -fno-elide-constructors, чтобы на самом деле вызвать конструктор копирования.)

Вы не можете решить эту проблему на C++ 03, где инициализация скобок всегда требует официальной копии. В C++ 11 вы можете использовать инициализацию скобками прямой инициализации элементов массива, хотя:

NotCopyable arr[] { {1, 0}, {2, 3} }; 

Это работает даже при отсутствии доступного конструктора копии.

+0

Похоже, ты прав. –

+0

Это не правило «как есть», поскольку для этого потребуется выход «КОПИРОВАНИЕ». Это исключение из 12.8/15 Стандарта, которое по существу расширяет правило «как есть», поэтому конструктор копирования не нужно вызывать при копировании временного объекта (даже если, как в этом случае, есть побочные эффекты в Копировать конструктор –

+0

@DavidThornley: Я знаю, я был немного кавалером по этому поводу. Действительно, законно ли удалять конструктор копирования *, даже если * имеет побочные эффекты. Я имел в виду нечто вроде обратного "как-если ", поскольку обе инициализации копии и direct-initialization-through-copy-construct подразумевают семантику« как будто »должен был быть вызван конструктор копирования (и копирование elision только начинается в процессе оптимизации). –

0
NotCopyable arr[] = 
{ 
    NotCopyable(1, 0), 
    NotCopyable(2, 3) 
}; 

При написании этого, потребность компилятора копировать-конструктор, как это копии инициализация. Вот почему вы получаете ошибку компиляции, поскольку конструктор-копия объявлен private, и поэтому он недоступен извне. Однако, если вы определяете его в разделе public, то он работает, но copy-constructor не получает вызов, что связано с оптимизацией, выполняемой компилятором. Спецификация позволяет компилятору выходить из вызова конструктора-копии в такой ситуации, однако ему все еще нужен доступный copy-constructor только для семантической проверки кода. Это действительно не называется, как только семантическая проверка сделана!

+0

Но как разрешить использование конструктора для инициализации, но запретить использование в других случаях? – Seagull

+0

Является ли копирование-инициализация или прямая инициализация с использованием экземпляра-копии? То есть«T arr [] = {T (1), T (2)},' интерпретируется как 'T arr1 (T (1)), arr2 (T (2));' или как 'T arr1 = T (1), arr2 = T (2); –

+0

@KerrekSB: Я думаю, что его копирование-инициализация. Но это кажется неактуальным, потому что в любом случае для семантической проверки необходим конструктор-копир. – Nawaz

1

Это неправильно, потому что вы используете массив объектов, которые должны быть созданы путем копирования:

#include <vector> 
using namespace std; 

class NotCopyable 
{ 
public: 
    NotCopyable(const double& attr1, const double& attr2) : _attr1(attr1), _attr2(attr2) {} 
    ~NotCopyable(void) {} 

private: 
    NotCopyable& operator=(const NotCopyable&); 
    NotCopyable(const NotCopyable&); 
    double _attr1; 
    double _attr2; 
}; 

int main() 
{ 
    vector<NotCopyable> v; 
    NotCopyable a(1, 2); 
    v.push_back(a); // THIS IS COPYING 
    return 0; 
} 

Поскольку вы отключили копирование, вы можете хранить только ссылку. Вы должны сделать это массив указателей :

NotCopyable a(1, 2); 

// incorrect: 
vector<NotCopyable> v; 
v.push_back(a); 

// correct: 
vector<NotCopyable*> v2; 
v2.push_back(&a); 

Надежда это помогает;)

Смежные вопросы