Правила программирования на Си и Си++

       

Классы, имеющие члены-указатели


Если класс не определяет методы копирования — конструктор копии и функцию operator=(), то это делает компилятор. Созданный компилятором конструктор должен выполнять "почленное" копирование, которое  осуществляется

 

таким  образом,  как

 

будто

 

вы  написали  this-field = src.field для каждого члена. Это означает, что теоретически должны вызываться конструкторы копий и функции operator=()

вложенных объектов и базовых классов. Даже если все работает правильно, все же указатели копируются как указатели. То есть, строка string, представленная как char*, —

не строка, а указатель, и будет скопирован лишь указатель. Представьте, что определение string на листинге 7 со страницы 155 не имеет конструктора копии или функции operator=(). Если вы запишите

string s1 = "фу", s2;

// ...

s2 = s1;

то это присваивание вместо поля указателя s2

запишет указатель от s1. Та память, которая была адресована посредством s1-buf, теперь потеряна, то есть у вас утечка памяти. Хуже того, если вы меняете s1, то

s2

меняется также, потому что они указывают на один и тот же буфер. Наконец, когда строки выходят из области действия, они обе передают buf для освобождения, по сути, очищая его область памяти дважды, и, вероятно, разрушают структуру динамической памяти. Решайте эту проблему путем добавления конструктора копии и функции operator=(), как было сделано на листинге 7 со страницы 155. Теперь копия будет иметь свой собственный буфер с тем же содержанием, что и у буфера строки-источника.

Последнее замечание: я выше написал "должен выполнять" и "теоретически" в первом абзаце, потому что встречал компиляторы, которые фактически выполняли функцию memcpy() в качестве операции копирования по умолчанию, просто как это бы сделал компилятор Си. В этом случае конструктор копии и функция operator=()

вложенных объектов не будут вызваны, и вы всегда

будете должны обеспечивать конструктор копии и функцию operator=() для копирования вложенных объектов. Если вы желаете достигнуть при этом абсолютной надежности, вы будете должны проделать это для всех классов, чьи члены не являются основными числовыми типами Си++.



Содержание раздела