LINUX.ORG.RU

C++ зачем делать в базовам классе закрым копирующий конструктор и присваивание ?


0

0

Зачем делать в базовом классе закрытыми копирующий конструктор и присваивание ?

Пример:
class CBase
{
private:
CBase(const CBase&);
CBase& operator=(const CBase&);
}

Класс CBase является базовым классом для всех класов, которые должны быть созданны на куче (с помощю оператора new).

Вопросики:

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

2. Их реализовывать в базовом классе нужно? Или достаточно объявить их закрытыми? И можно оставить их без определения?

==Какой же неблагодарный труд -- работать спеллчекером... Obidos.

anonymous

1. Зачастую это делается для того, чтобы исключить неявное копирование объекта (на стек, например). Причины могут быть разные -- или объект очень большой (тогда копировать -- терять производительность), или объект содержит указатели на данные, выделенные с помощью new, которые в деструкторе удаляет (тогда, если объект неявно скопируется, то delete вызовется дважды для одного указателя).

2. Нет, реализовывать не обязательно.

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

> 1. Зачастую это делается для того, чтобы исключить неявное копирование объекта (на стек, например). Причины могут быть разные -- или объект очень большой (тогда копировать -- терять производительность), или объект содержит указатели на данные, выделенные с помощью new, которые в деструкторе удаляет (тогда, если объект неявно скопируется, то delete вызовется дважды для одного указателя).

..или же объект содержит члены, которые вообще нельзя копировать (как правило).

// wbr

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

>которые в деструкторе удаляет (тогда, если объект 
>неявно скопируется, то delete вызовется дважды для 
>одного указателя).

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

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

> При копировании создасться объект, выделится память
> под то, что нужно, затем удалится при разрушении объекта.
> Почему delete должно вызываться дважды для одного указателя ?
> А что значит: " неявно скопируется " ?
> я не понял или забыл уже. :(

struct file {
FILE* f;
file(const char* name) { f = fopen(f, "r+"); }
~file() { fclose(f); }
...
}

file f1("/dev/null"), f2 = f1;
...
// fclose вызовется дважды на один дескриптор

C delete и любым другим случаем, где требуется ручное управление
ресурсами, та же бяка. Иногда можно написать ручками корректный
конструктор копирования и operator=. Иногда, как в этом примере, класс
просто не имеет внятной однозначной семантики копирования - тогда и
используется private.

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

Похоже понятно. Да действительно, в таком случае 
произойдет побайтное копирование и ещё один указатель 
будет указывать на один и тот-же объект, спасибо.
Только не ясно зачем писать код порождающий потенциальную ошибку ? почему не прописывать конструктор копирования, оператор присваивания, для нормальной процедуры копирования объекта ? Уменьшение кода - весьма сомнительно, зачем такое делают ?

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

> объект, спасибо. Только не ясно зачем писать код порождающий
> потенциальную ошибку ? почему не прописывать конструктор копирования,
> оператор присваивания, для нормальной процедуры копирования объекта ?

Я же объяснил - не всегда есть вменяемая семантика копирования для
класса. Например, тот же struct file - вот что он должен делать при
копировании?

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

Как верно написали klalafuda и int19h, иногда копирование просто не является семантически допустимой операцией для объектов данного класса. Для того и запрещают, чтобы соблазна не было. Во многих ОО-языках неявно копироваться могут только ссылки (но не объекты). Т.е. хочешь скопировать объект -- пишешь copy, или clone, или что-нибудь подобное.

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

Хм..., вопрос. навеерное в зависимости от того, для 
чего он создается. Но по идее, должен открывать файл,
 а не копировать указатель. Но действительно, это не очевидно и кроме этого требует дополнительных ресурсов, например имя файла. :( Но это вопрос уже больше не реализации, а проектирования. Мне честно говоря не очень понятно зачем в конкретном примере 
присваивать указателю чужое значение. Копия - это 
дубль, ещё один экземпляр, без пересечения, а в данном
случае получается пересечение по данным. Поэтому я и
задал вопрос.
Вот например строка - ведь не присваивается же там 
указатель, а нормально создается дубль с выделением 
памяти, все логично. Здесь то-же вроде нужно открыть 
файл функцией open() или fopen() 
В деструкторе закрыть соответственно.

> иногда копирование просто не является семантически 
> допустимой операцией для объектов данного класса. 
> Для того и запрещают, чтобы соблазна не было.

Я поонял почему запрещают, зачем private.
Я не понял почему " не всегда есть вменяемая семантика
копирования для класса ", допускаю что есть, но немогу 
понять когда, вопрос конечно не однозначный. 
Мне кажется нужно создавать так, чтоб было всё 
вменяемо, не так ?

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

ну замените открытый файл на что-то более чувствительне к копированию.
например, POSIX семафор или мъютекс, поведение которых "undefined"
при простом копировании скажем sem_t в sem_t.

http://www.opengroup.org/onlinepubs/009695399/functions/sem_init.html

---cut---
Only sem itself may be used for performing synchronization.
The result of referring to copies of sem in calls to sem_wait(),
[TMO] [Option Start] sem_timedwait(), [Option End] sem_trywait(),
sem_post(), and sem_destroy() is undefined.
---cut---

что вы будете делать в конструкторе копирования для:

class Semaphore {
public :
    Semaphore(int pshared = 0, unsigned value = 0) {
        sem_init(&sem_, pshared, value);
    }
    ~Semaphore {
        sem_destroy(&sem_);
    }
    Semaphore(const Semaphore &src) {
        sem_ = src.sem_; // ???
    }

private :
    sem_t sem_;
};

// wbr

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

Семафор - разделяемый объект, соответственно должен присоединяться. Наверно сам объект должен отрабатывать ту или другую операцию согласно своей специфики. Возможно, такие общие объекты должны инкапсулировать счетчики, которые следят за закрытием ресурса ядра. Возможно ещё какие-то переменные и возможно принадлежащие классу, которые помогают разруливать такие ситуации.

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

> Семафор - разделяемый объект, соответственно должен присоединяться.

пардон, но он разделяем лишь между нитями исполнения (потоки/процессы) но не местоположением в памяти :) использовать семафор порожденный через sem_init() в другом потоке? пожалуйста. но делать копию семафора - нет.

> Наверно сам объект должен отрабатывать ту или другую операцию согласно своей специфики.

AFAIK по крайней мере в POSIX нет методов клонирования семафоров. можно конечно попытаться сделать счетчик и оперировать указателем на уже проинициализированный семафор, но IMHO это хак -> некорректное поведение.

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

// wbr

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

А зачем делать копию семафора или другого объекта не предназначенного для этого ? Нельзя-же объект написать на все случаи жизни и сделать защиту от дурака или это нужно ? Семафор используется для специфических действий для синхронизации процессов (задач) ну так и пусть объект делает это в конце концов можно определить пустой конструктор копирования или сгенерить исключительную ситуацию при попытке использования не по назначению или нельзя ? Конечно такой ресурс нельзя копировать. Будет что-то непонятное. А есть такая ситуация, когда его захочется/могут копировать ?

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

> А зачем делать копию семафора или другого объекта не предназначенного для этого ?

по незнанию или случайно? например, попробовать сделать std:list<Semaphore> без какого-то особого злого умысла..

> Нельзя-же объект написать на все случаи жизни и сделать защиту от дурака или это нужно ?

сделать на все случаи жизни наверное нельзя, а вот сделать защиту от дурака, в особенности, если это почти задаром, никогда не помешает :)

> Семафор используется для специфических действий для синхронизации процессов (задач) ну так и пусть объект делает это в конце концов можно определить пустой конструктор копирования или сгенерить исключительную ситуацию при попытке использования не по назначению или нельзя ? Конечно такой ресурс нельзя копировать. Будет что-то непонятное. А есть такая ситуация, когда его захочется/могут копировать ?

согласитесь, отлавливать ошибки на этапе компиляции значительно проще и дешевле, чем в процессе исполнения :)

// wbr

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

> согласно своей специфики. Возможно, такие общие объекты должны
> инкапсулировать счетчики, которые следят за закрытием ресурса
> ядра.Возможно ещё какие-то переменные и возможно принадлежащие
> классу, которые помогают разруливать такие ситуации.

Да, в общем случае всегда можно сделать счетчик ссылок. Но! это не
задача класса. Для этого есть boost::shared_ptr и тому подобные вещи.
Дублировать этот код в каждом классе - бессмысленно и вредно.

Просто надо понять, что не для всех сущностей задана операция
копирования. Вот и все. И C++ (в отличие от некоторых других языков)
позволяет отметить это явно, путем помещения конструктора копирования
и operator= в private-секцию.

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

> а вот сделать защиту от дурака, в особенности, если 
> это почти задаром, никогда не помешает :)

Этот самый товарищь просто в Вашем классе возьмет и 
переправит private на public. Уже через это проходили.
 
>согласитесь, отлавливать ошибки на этапе компиляции 
>значительно проще и дешевле, чем в процессе 
>исполнения :)

Проще.

>Просто надо понять, что не для всех сущностей задана 
>операция копирования.

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

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

> Этот самый товарищь просто в Вашем классе возьмет и
> переправит private на public. Уже через это проходили.

Нет, это точно не лечится.

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

Можно, и тебе подробно описали, как. "Возможности данного языка" - это
неизбежная данность, которую _приходится_ использовать. Правильный
дизайн - это "данный объект нельзя копировать". Корректная его
реализацация на C++ была описана выше. Если это не доходит, то видит
Эру, дальше только биореактор.

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