Проверить "самостоятельное назначение" в конструкторе копирования?
Сегодня в университете мне рекомендовали профессором, что я проверил бы (this != ©)
в конструкторе копирования, аналогично тому, как вы должны это делать при перегрузке operator=
. Однако я поставил под сомнение это, потому что я не могу представить ни одной ситуации, когда this
когда-либо был бы равен аргументу при построении объекта.
Он признал, что я сделал хороший момент. Итак, мой вопрос заключается в том, имеет ли смысл выполнять эту проверку, или это невозможно, чтобы это испортило?
Изменить: Я думаю, я был прав, но я просто оставлю это открытым некоторое время. Может быть, кто-то придумает какую-нибудь сумасшедшую загадочную магию С++.
Edit2: Test a(a)
компилируется на MinGW, но не MSVS10. Test a = a
компилируется на обоих, поэтому я предполагаю, что gcc будет вести себя как-то похоже. К сожалению, VS не показывает отладочное сообщение с "Variable a used without initialized". Тем не менее, это сообщение действительно отображается для int i = i
. Может ли это считаться недостатком языка С++?
class Test
{
Test(const Test ©)
{
if (this != ©) // <-- this line: yay or nay?
{
}
}
Test &operator=(const Test &rhd)
{
if (this != &rhd) // <-- in this case, it makes sense
{
}
}
};
Ответы
Ответ 1
Лично я считаю, что ваш профессор ошибается, и вот почему.
Конечно, код будет компилироваться. И конечно, код сломан. Но что касается вашего профессора с его рассуждениями, а затем он приходит к выводу: "О, хорошо, мы должны убедиться, что мы самонадеянны, а если да, то просто вернемся".
Но это плохо, по той же причине, почему глобальный catch-all catch(...)
, который ничего не делает, - это Evil. Вы предотвращаете непосредственную проблему, но проблема все еще существует. Недопустимый код. Вы не должны вызывать конструктор с указателем на себя. Решение не должно игнорировать проблему и продолжать ее. Решение состоит в том, чтобы исправить код. Лучшее, что может произойти, - это немедленно свернуть ваш код. Хуже всего то, что код будет продолжаться в недопустимом состоянии в течение некоторого периода времени, а затем либо сбой позже (когда стек вызовов не принесет вам пользы), либо создаст недопустимый вывод.
Нет, твой профессор ошибается. Выполняйте задание без проверки на самоопределение. Найдите дефект в обзоре кода или сообщите об ошибке кода и найдите его в сеансе отладки. Но не продолжайте так, как будто ничего не произошло.
Ответ 2
Это действительный С++ и вызывает конструктор копирования:
Test a = a;
Но это не имеет смысла, потому что a
используется до его инициализации.
Ответ 3
Ваш инструктор, вероятно, пытается избежать этой ситутации -
#include <iostream>
class foo
{
public:
foo( const foo& temp )
{
if( this != &temp )
std::cout << "Copy constructor \n";
}
};
int main()
{
foo obj(obj); // This is any how meaning less because to construct
// "obj", the statement is passing "obj" itself as the argument
}
Так как имя (т.е. obj
) отображается во время объявления, код компилируется и действителен.
Ответ 4
Если вы хотите быть параноиком, то:
class Test
{
Test(const Test ©)
{
assert(this != ©);
// ...
}
};
Вы не хотите продолжать, если this == ©
. Я никогда не беспокоился об этой проверке. В коде, с которым я работаю, часто возникает ошибка. Однако, если ваш опыт отличается от того, что утверждение может стоить того.
Ответ 5
Ваш инструктор может подумать о проверке самозадачи в операторе присваивания копии.
Рекомендуется проверить самоопределение в операторе присваивания как в Sutter, так и Alexandrescu "Стандарты кодирования С++", а Скотт Майер ранее "Эффективный С++".
Ответ 6
В нормальных ситуациях, похоже, здесь нет необходимости. Но рассмотрим следующую ситуацию:
class A{
char *name ;
public:
A & operator=(const A & rhs);
};
A & A::operator=(const A &rhs){
name = (char *) malloc(strlen(rhs.name)+1);
if(name)
strcpy(name,rhs.name);
return *this;
}
Очевидно, что приведенный выше код имеет проблему в случае, когда мы выполняем самостоятельное присвоение. Прежде чем мы сможем скопировать содержимое, указатель на исходные данные будет потерян, так как оба они ссылаются на один и тот же указатель. Вот почему нам нужно проверить самоопределение. Функция должна быть как
A & A::operator=(const A &rhs){
if(this != &rhs){
name = (char *) malloc(strlen(rhs.name)+1);
if(name)
strcpy(name,rhs.name);
}
return *this;
}
Ответ 7
При написании операторов присваивания и конструкторов копирования всегда выполните следующие действия:
struct MyClass
{
MyClass(const MyClass& x)
{
// Implement copy constructor. Don't check for
// self assignment, since &x is never equal to *this.
}
void swap(MyClass& x) throw()
{
// Implement lightweight swap. Swap pointers, not contents.
}
MyClass& operator=(MyClass x)
{
x.swap(*this); return *this;
}
};
При передаче x
по значению оператору присваивания выполняется копия. Затем вы поменяете его на *this
, и пусть x
деструктор будет вызван при возврате, со старым значением *this
. Простой, изящный, безопасный, без дублирования кода, и нет необходимости проводить самоопределение.
Если вы еще не знаете об исключениях, вы можете запомнить эту идиому при обучении безопасности исключений (и игнорировать спецификатор throw()
для swap
).
Ответ 8
Я согласен, что самопроверка не имеет никакого смысла в конструкторе копирования, поскольку объект еще не создан, но ваш профессор прав насчет добавления проверки, чтобы избежать дальнейших проблем. Я пробовал с помощью/без самопроверки и получил неожиданный результат, когда не проверяется самоконтроль и ошибка времени выполнения, если существует самоконтроль.
class Test
{
**public:**
Test(const Test& obj )
{
size = obj.size;
a = new int[size];
}
~Test()
{....}
void display()
{
cout<<"Constructor is valid"<<endl;
}
**private:**
}
При создании конструктора копирования и вызываемой функции-члена я didn
Test t2(t2);
t2.display();
Вывод:
Внутренний конструктор по умолчанию
Внутренний параметризованный конструктор
Внутренний конструктор копии
Конструктор действителен
Это может быть корректно синтаксически, но выглядит неправильно.
При самообслуживании я получил ошибку времени выполнения, указывающую на ошибку в коде, чтобы избежать такой ситуации.
Test(const Test& obj )
{
if(this != &obj )
{
size = obj.size;
a = new int[size];
}
}
Ошибка выполнения:
Ошибка в `/home/bot/1eb372c9a09bb3f6c19a27e8de801811 ': munmap_chunk(): недопустимый указатель: 0x0000000000400dc0
Ответ 9
Как правило, оператор = и конструктор копирования вызывает функцию копирования, поэтому возможно самозаряжение.
Итак,
Test a;
a = a;
Например,
const Test& copy(const Test& that) {
if (this == &that) {
return *this
}
//otherwise construct new object and copy over
}
Test(const &that) {
copy(that);
}
Test& operator=(const Test& that) {
if (this != &that) { //not checking self
this->~Test();
}
copy(that);
}
Выше, когда выполняется a = a, вызывается перегрузка оператора, которая вызывает функцию копирования, которая затем обнаруживает самоопределение.