Форум программистов, компьютерный форум CyberForum.ru

copy constructor operator= - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 15, средняя оценка - 4.67
LosAngeles
Заблокирован
03.08.2011, 15:54     copy constructor operator= #1
сижу я значит почитываю черновик будущего стандарта и вижу
C++
1
2
3
4
5
6
7
const C& C::operator=( const C& other) {
      if ( this != &other ) {
           this->~C();
           new (this) C(other);
      }
return *this;
}
оператор=, реализованный через конструктор копирования и написанный в две строчки. Я обычно сначала зачищал старое и присваивал новое ну как сказать почленно и не я один, а тут такая гениальная вроде конструкция. Поэтому я подумал, что тут наверняка есть подводный камень, но пока не могу сообразить какой именно? Там правда несколько ограничений из которых существенно наверно только это
the original object was a most derived object (1.8) of type T and the new object is a most derived
object of type T (that is, they are not base class subobjects).
то есть юзать можно?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
03.08.2011, 15:54     copy constructor operator=
Посмотрите здесь:

this и Copy-Constructor C++
Copy-Constructor В Шаблонном Связном списке C++
C++ Deep copy and Shadow copy
C++ Copy Constructor Question .Generic inheritance. Templates
operator char() или operator int() C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
03.08.2011, 16:17     copy constructor operator= #2
Странно, в операторе присваивания вызывается конструктор. Ни в одной из прочитанных мною книг, не говорилось о подобной реализации

Добавлено через 41 секунду
Я так понял, речь об операторе, генерируемом по умолчанию?
silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 16:41     copy constructor operator= #3
эх, когда-то реализовывал умный указатель, и как раз писал такую конструкцию, но компилятор меня обрубил, сказав что конструктор нельзя вызвать явно, пришлось обходить другими путями)
LosAngeles
Заблокирован
03.08.2011, 16:55  [ТС]     copy constructor operator= #4
наверно ты что-то не так делал, потому что тут нету вобщем то ничего, что противоречит и 2003, или я по крайней мере не вижу что могло бы помешать компиляции...
Цитата Сообщение от Kastaneda Посмотреть сообщение
Странно, в операторе присваивания вызывается конструктор.
это можно и не только для placement new делать, с обычным new ведь тоже можно так делать?
C++
1
2
string p("yrtyrt");
string *ptr = new string(p);
silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 17:04     copy constructor operator= #5
LosAngeles, ну я пытался явно через this вызывать, что не дало сделать.
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
03.08.2011, 17:04     copy constructor operator= #6
LosAngeles, да я не про это, там же запятая в предложении стоит)

Я про то, что странно, что внутри оператора присваивания вызывается конструктор.
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
03.08.2011, 17:06     copy constructor operator= #7
да и деструктор как-то не очень
может это псевдокод?
Kastaneda
03.08.2011, 17:07
  #8

Не по теме:

Цитата Сообщение от silentnuke Посмотреть сообщение
но компилятор меня обрубил, сказав что конструктор нельзя вызвать явно
Да, там (в первом посте) по хитрому сделано, надо будет взять этот прием на вооружение)

silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 17:08     copy constructor operator= #9
Цитата Сообщение от Kastaneda Посмотреть сообщение
LosAngeles, да я не про это, там же запятая в предложении стоит)

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

Добавлено через 30 секунд
Цитата Сообщение от Kastaneda Посмотреть сообщение

Не по теме:


Да, там (в первом посте) по хитрому сделано, надо будет взять этот прием на вооружение)

угу, и я про это же)
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
03.08.2011, 17:11     copy constructor operator= #10
Цитата Сообщение от OstapBender Посмотреть сообщение
да и деструктор как-то не очень
может это псевдокод?
По поводу псевдокода, я так понял - это шутка)

А данная форма вызова деструктора, если верить Б.Эккелю ("Философия С++"), возможна только если память под объект была выделенна placement new.

Добавлено через 1 минуту
Цитата Сообщение от silentnuke Посмотреть сообщение
ведь копи конструктор, и присваивание по своей сути тоже самое.
По сути да, но концептуально это разные операции. А тут как то все вместе...
silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 17:12     copy constructor operator= #11
Цитата Сообщение от Kastaneda Посмотреть сообщение
По поводу псевдокода, я так понял - это шутка)

А данная форма вызова деструктора, если верить Б.Эккелю ("Философия С++"), возможна только если память под объект была выделенна placement new.
как раз деструктор можно явно вызывать, пока не встречал случаев, где не получилось бы его явно вызвать)
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
03.08.2011, 17:15     copy constructor operator= #12
Цитата Сообщение от silentnuke Посмотреть сообщение
пока не встречал случаев, где не получилось бы его явно вызвать)
да, но зачем?

мне стыдно но я не знаю что такое placement new (( видимо когда перегружается оператор new или типа того
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
03.08.2011, 17:17     copy constructor operator= #13
Цитата Сообщение от silentnuke Посмотреть сообщение
как раз деструктор можно явно вызывать
Да, пардон, имелось ввиду - что явный вызов деструктора оправдан, только если ...(далее по тексту)
В общем не подумав написал) В данном коде явный вызов тоже оправдан, если считать разумным вызов деструктора в операторе =
silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 17:18     copy constructor operator= #14
Цитата Сообщение от Kastaneda Посмотреть сообщение
По поводу псевдокода, я так понял - это шутка)

А данная форма вызова деструктора, если верить Б.Эккелю ("Философия С++"), возможна только если память под объект была выделенна placement new.

Добавлено через 1 минуту

По сути да, но концептуально это разные операции. А тут как то все вместе...
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
        SM(const SM<T>& k)
    {
        _count=k._count;
        ++(*_count);
        _data=k._data;
    }
        ~SM()
    {
        --(*_count);
        if(!(*_count)){
            if(_data)
                delete _data;
            delete _count;
            _data=NULL;
            _count=NULL;
        }
    }
    SM<T> operator=(SM<T>& obj)
    {
        this->~SM();
        _count=obj._count;
                ++(*_count);
        _data=obj._data;
        return *this;
    }
вот примерный код, где хотел вызвать явно конструктор копирования)
если б тогда знал про эту фичу, не было бы никакого смысла дублировать код копирования.=)
пример довольно грубый, написанный на коленке)
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
03.08.2011, 17:21     copy constructor operator= #15
Цитата Сообщение от OstapBender Посмотреть сообщение
мне стыдно но я не знаю что такое placement new
C++
1
2
3
4
    int mas[10];
    int *ptr=new(mas) int;//вместо mas можно задать и число, если понимаешь, что делаешь)
    *ptr=10;
    std::cout<<mas<<" "<<ptr<<" "<<mas[0]<<" "<<*ptr<<std::endl;
silentnuke
Android Programmer
137 / 138 / 5
Регистрация: 08.12.2010
Сообщений: 421
03.08.2011, 17:22     copy constructor operator= #16
Цитата Сообщение от OstapBender Посмотреть сообщение
да, но зачем?

мне стыдно но я не знаю что такое placement new (( видимо когда перегружается оператор new или типа того
а кто говорит что это нужно повседневно использовать?)
просто бывают ситуации, когда это удобней.
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
03.08.2011, 17:34     copy constructor operator= #17
Kastaneda, всего то размещение в указанной пользователем области памяти ) я думал что-то страшное. буду знать.

silentnuke, верю. но сам не сталкивался ... хотя о чём я говорю, я даже не работал еще нигде
Kastaneda
03.08.2011, 17:40
  #18

Не по теме:

Цитата Сообщение от OstapBender Посмотреть сообщение
я думал что-то страшное. буду знать.
Но полезно знать, что так можно делать)

LosAngeles
Заблокирован
03.08.2011, 17:44  [ТС]     copy constructor operator= #19
Цитата Сообщение от Kastaneda Посмотреть сообщение
А данная форма вызова деструктора, если верить Б.Эккелю ("Философия С++"), возможна только если память под объект была выделенна placement new.
C++
1
2
3
4
5
6
7
8
9
10
int main(){
    string p("yrtyrt");
    string *pr = &p;
 
    p.~basic_string();
    new(pr) string("dfgdfg");
 
 
    return 0;
}
я проверил, вроде для placement new неважно объект был порождён другой формой new или создан в стеке. И в том примере с оператором this ведь может и на стек указывать, тем не менее никаких SIGABRT я не получал от системы. Тут наверно главное соблюдать "равновесие" конструкторов и деструкторов

Цитата Сообщение от silentnuke Посмотреть сообщение
как раз деструктор можно явно вызывать, пока не встречал случаев, где не получилось бы его явно вызвать)
да, но главное соблюдать осторожность. Язык сам неявно вызовет деструктор для статических объектов при терминации, а для локальных при выходе из scope(блин забыл как это по русски?). Если такие объекты уничтожаешь явно, наверно надо не забыть запилить туда другой такой же объект, как в этом примере.
Такое применение placement new и явного вызова ~ я первый раз кстати встретил. Единственный пример который видел до этого был как раз наоборот. Где то выделялось pnew и потом для этого деструктор явно вызывался
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.08.2011, 17:52     copy constructor operator=
Еще ссылки по теме:

что делает copy constructor? C++
C++ Copy constructor
Реализация operator + через operator += C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
ValeryLaptev
Эксперт C++
1005 / 784 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
03.08.2011, 17:52     copy constructor operator= #20
Вот ликбез по placement new/


Существует еще одна форма операции new, которая позволяет разделить «во времени и пространстве» операции выделения памяти и конструирования объекта в этой памяти. Эта форма называется «размещающий» new (placement new) и тоже работает только при подключении заголовка
C++
1
#include <new>
В стандарте [1-18.4] этот вид операции определен так:
C++
1
2
void *operator new(std::size_t size, void *ptr) throw();
void *operator new[](std::size_t size, void *ptr) throw();
Как видите, эта форма оператора тоже исключений не вызывает. Второй аргумент — это указатель на уже выделенный каким-нибудь образом буфер памяти. Действие этой операции похоже на union, но «склеивание» адресов выполняется во время работы программы, а не при компиляции.
В стандарте определены и соответствующие функции delete:
C++
1
2
void operator delete(void* ptr, void*) throw();
void operator delete[](void* ptr, void*) throw();
Однако явным образом их вызывать не требуется.
Рассмотрим несколько простых примеров, чтобы было понятны способы применения этого вида операции new.
C++
1
2
3
4
5
6
int *i = new int[10];                   // выделели память
i[0] = 21;   cout << i[0] << endl;
int *j = new(i) int[10];                // placement new
cout << j[0] << endl;                   // j[0] == i[0] 
j[0] = 22;   cout << i[0] << endl;      // i[0] == j[0]
delete [] i;
//
Здесь сначала создается динамический массив i, и нулевому элементу присваивается значение 21. Затем размещающий new «приклеивает» по этому же адресу другой динамический массив j. После этого любые изменения в массиве i синхронно изменяют массив j и наоборот. Обратите внимание — у нас два указателя на одну и ту же память, а не два разных массива! Поэтому удалять такой динамический массив нужно только один раз.
Вместо динамического массива мы вполне можем использовать обычный массив, например
C++
1
2
3
4
int i[10] = { 1,2,3,4,5,6,7,8,9,0};     // выделели память
int *j = new(i) int[10];                // placement new
cout << j[0] << endl;                   // j[0] == i[0] 
j[0] = 22;   cout << i[0] << endl;      // i[0] == j[0]
В этом случае размещаемый массив j удалять нельзя. Однако компилятор опять не выдаст никакого сообщения, если программист нечаянно напишет оператор удаления, например
C++
1
delete[] j;
При выполнении опять же может случиться все, что угодно.
Исходный объект и размещаемый, конечно, могут быть разного типа и размера. Но тут нужно быть осторожным, так как компиляторы просто не выдают никаких предупреждающих сообщений .
C++
1
int m; char *pm = new(&m) char;
Этот случай безопасен, так как размещаемая величина меньше по размеру, чем «база». А вот такой вариант
C++
1
char t; double *pt = new(&t) double();
чреват неприятностями. При компиляции сообщений никаких не выдается, поэтому во время работы может произойти все, что угодно. И тут уж как повезет.
Отсутствуют сообщения о преобразовании типов и в случае использования массивов:
C++
1
2
3
int tt[4]={0};  double *p = new(tt)double[4];
double *t = new double[4];  int *j = new (t) int[4];
double m[4];  j = new (m) int[4];
Обратите внимание — в размещающем new в этом случае не нужно указывать операцию взятия адреса &, так как имя массива уже по умолчанию является адресом первого элемента. Конечно, можно «установиться» на любой элемент массива:
C++
1
double *p = new(&tt[1])double[4];
Естественно, в этом случае нужно задавать адрес элемента.
Отсутствие сообщений о том, что типы разные, может спровоцировать ошибки, например:
C++
1
p[0] = 1.2;  cout << p[0] << endl;  cout << tt[0] << endl;
В этом фрагменте мы меняем значение элемента массива типа double, размещенное поверх целого массива. В результате меняется и значение элемента целого массива. Ситуация совершенно аналогична тому, что происходит при использовании union.

Можно поверх скалярной величины разместить массив, и поверх массива — скалярную величину, например
C++
1
2
int m; char *pm = new(&m) char[sizeof(int)];
char rr[sizeof(int)]; int *ptt = new (rr) int(50);
Сравните это с объявлением union
C++
1
2
3
4
union A
{   int m;
    char pm[sizeof(int)];
};
В обоих случаях происходит размещение целой переменной m и символьного массива pm по одному адресу, однако размещаемый new это выполняет во время работы программы, а при использовании union — это делает компилятор.
В принципе, использование размещаемого new в этих случаях эквивалентно использованию обычного указателя, например
C++
1
int *j = &i[0];
Однако такая запись менее заметна (и поэтому провоцирует больше ошибок), чем использование размещающего new.

Размещающий new обычно применяется как раз не для встроенных типов. Еще раз напомним, что работа «обычного» new состоит из двух операций: выделение памяти и вызов конструктора для конструирования объекта. Память размещающий new не выделяет, но вызов конструктора выполняется и в этом случае. Но тогда возникает проблема вызова деструктора — для «обычных» динамических объектов деструктор вызывался автоматически операцией delete. Однако для размещающего new операцию delete выполнять не нужно, поэтому и деструктор автоматически не вызывается!
Решение очень простое: нужно вызвать деструктор явно [1-12.4/13]. Рассмотрим пример (листинг 8.5).
C++
1
2
3
4
5
6
7
8
9
10
Листинг 8.5. Явный вызов деструктора
class Integer                           // некоторый класс
{ int i;
  public: 
    Integer() { cout << "constructor" << endl; }
    ~Integer() { cout << "destructor" << endl; }
};
// …
int m;                              // память для объекта
Integer *pi = new(&m) Integer;      // размещение объекта
pi->~Integer(); // разрушение объекта
Мы объявили простой класс, размер которого равен размеру целого. Размещение делается обычным способом. А последняя строка как раз и является явным вызовом деструктора. Обратите внимание, что и в данном случае преобразования типов не требуется.
Заменим в классе Integer конструктор по умолчанию конструктором инициализации:
C++
1
Integer(int k=0):i(k) { }
Тогда можно задавать размещение объекта с инициализацией, например
C++
1
Integer *pi = new(&m) Integer(6);
Если уж нам приходится «работать компилятором», то нужно помнить, что при уничтожении массива требуется вызывать деструктор для каждого его элемента.
C++
1
2
3
int tt[4]; 
pi = new(tt) Integer[4];                
for(int i = 0; i < 4; ++i) (pi+i)->~Integer();
При размещении массива типа Integer каждый из элементов инициализируется 0, поскольку вызывается конструктор инициализации с аргументом по умолчанию.
Вызов деструктора написан в указательном стиле. Можно использовать и привычный синтаксис с индексом, например
C++
1
for (int k = 0; k < 4; ++k) pi[k].~Integer();
До сих пор мы в качестве «базы» размещаемого new использовали только встроенные типы, однако это совершенно не обязательное условие — тип «основы» может быть любым. Например, определим небольшой шаблон
C++
1
2
3
4
template <class T, int size = sizeof(T)>
class DynamicUnion
{   char s[size];
};
При инстанцировании объект конкретного типа займет ровно столько байт памяти, каков размер типа T. Теперь мы можем использовать этот шаблон для размещения объектов типа Integer
C++
1
2
3
4
5
DynamicUnion<Integer> t;
Integer *pt = new(&t) Integer(7);
pt->~Integer();
Деструктор можно вызывать и так
(*pt).~Integer();
Разрешается «разделить» запрос памяти и конструирование в ней объекта, например
C++
1
2
3
4
5
void *p = new DynamicUnion<Integer>;    // выделили sizeof(Integer) байт  
new (p) Integer(7);                     // разместили объект  
((Integer*)p)->~Integer();              // удалили объект
Оператор
new (p) Integer(7);                     // разместили объект
представляет собой «объявление» объекта типа Integer. Вызывается конструктор класса Integer и размещает поля объекта по указанному адресу, выполняя их инициализацию.

В данном случае при выделении памяти использован бестиповый указатель. Ни запрос памяти, ни размещение по этому адресу объекта не требуют преобразования типа. А вот разрушение объекта без преобразования указателя вызывает протесты компилятора — C++ Builder 6 выдает ошибку E2288:
Pointer to structure required on left side of -> or ->*
Слева от -> или ->* требуется указатель на структуру
Поскольку р не является типизированным указателем на структуру, «возмущение» компилятора совершенно обосновано.

Точно так же с явным преобразованием типа необходимо обращаться к методам размещенного объекта. Пусть, например, в классе Integer определен метод:
C++
1
2
void print() 
{ cout << "print()="<< i << endl; }
Тогда обращение к нему должно выглядеть так:
C++
1
((Integer*)p)->print();
Если мы при запросе памяти объявим указатель типа Integer, то при обращении, естественно, преобразования не потребуется, например
C++
1
2
3
4
Integer *p = (Integer*)new DynamicUnion<Integer>;   // преобразование здесь
new (p) Integer(15);                                // размещение объекта
p->print();
p->~Integer();
Обратите внимание, что этом фрагменте мы имеем единственный указатель, получивший адрес объекта в динамической памяти. Можно обойтись вообще без динамической памяти, например
C++
1
2
int m; 
new(&m) Integer(6);
Объект типа Integer создан и проинициализирован по месту переменной m. Тогда возникает вопрос: как же тогда вызывать деструктор такого объекта? А вот так:
C++
1
((Integer*)&m)->~Integer();
или так
C++
1
(*(Integer*)&m).~Integer();
К сожалению, с массивами размещающий new не всегда работает правильно (хотя это могут быть недоработки конкретных компиляторов). Рассмотрим простейший пример
C++
1
2
3
int tt[4]; 
Integer *pi = new(tt) Integer[4];   // корректный вызов конструктора
new(tt) Integer[4];                 // некорректный вызов конструктора
Вариант с присвоением адреса указателю pi работает совершенно правильно, вызывая четыре конструктор со значением по умолчанию, равному 0. А вот второй вариант инициализирует первый элемент массива значением 4, а остальные — обнуляет.
Yandex
Объявления
03.08.2011, 17:52     copy constructor operator=
Ответ Создать тему

Метки
copy operator=
Опции темы

Текущее время: 04:37. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru