почему мы должны отправлять константную ссылку "type" вместо простого имени типов в конструктор - PullRequest
1 голос
/ 21 мая 2010

я пытаюсь создать простую программу (и да, это домашнее задание), которая может генерировать даты, и, как и большинство обычных людей: я сделал свои атрибуты класса закрытыми, я попытался отправить тот же тип, над которым я работаю конструктор, но компилятор не принял его, я провел некоторое исследование и обнаружил, что в подобных случаях люди щедро посылают константную «типовую» ссылку на конструктора, означающего для меня, что они плохо понимают ООП
так почему мы должны отправлять константную ссылку "type" вместо простого имени типов в конструктор? и, пожалуйста, дайте мне несколько ссылок или веб-сайтов для начинающих
вот мир моего Кодекса:

 class Date {  
     int d ;   
     int m ;   
     int y ;   
 public :   
      Date();   
      Date(int , int , int);   
      Date(const Date &);// my question is : why do we have to write this instead of Date( Date )   
 };

PS: простите за мой английский

Ответы [ 4 ]

6 голосов
/ 21 мая 2010

Перефразируя наш вопрос:

почему мы должны писать Date(const Date &) вместо Date(Date)?

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


Причина, по которой конструктор копирования должен принимать свой аргумент для каждой ссылки, состоит в том, что для функции, которая принимает аргумент для каждой копии void f(T arg), когда вы вызываете ее f(obj), obj - это скопировано в arg с использованием конструктора копирования T . Поэтому, если вы хотите реализовать конструктор копирования, вам лучше не принимать аргумент за копированием, потому что это вызовет конструктор копирования при его вызове, что приведет к бесконечной рекурсии. Вы можете легко попробовать это сами:

struct tester {
  tester(tester) {std::cout << "inside of erroneous copy ctor\n";}
};

int main()
{
  tester t1;
  std::cout << "about to call erroneous copy ctor\n";
  tester t2(t1);
  std::cout << "done with call erroneous copy ctor\n";
  return 0;
}

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

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


Это оставляет вопрос почему это const T&, а не просто T&? На самом деле, для этого есть две причины.
Логическая причина заключается в том, что при вызове конструктора копирования вы не ожидаете, что скопированный объект изменится. В C ++, если вы хотите выразить, что что-то является неизменным, вы используете const. Это говорит пользователям, что они могут безопасно передавать свои драгоценные объекты вашему конструктору копирования, потому что он не будет ничего с ним делать, кроме чтения из него. В качестве приятного побочного эффекта, если вы реализуете конструктор копирования и случайно попытаетесь записать объект, компилятор выдаст вам сообщение об ошибке, напоминающее вам об обещании, сделанном для вызывающей стороны.
Другая причина в том, что вы не можете привязать временные объекты к ссылкам не const, вы можете привязать их только к const ссылкам. Например, временный объект - это то, что может вернуть функция:

struct tester {
  tester(tester& rhs) {std::cout << "inside of erroneous copy ctor\n";}
};

tester void f()
{
   tester t;
   return t;
}

Когда вызывается f(), внутри него создается объект tester, и его копия затем возвращается вызывающей стороне, которая затем может поместить ее в другую копию:

tester my_t = f(); // won't compile

Проблема в том, что f() возвращает временный объект, и чтобы вызвать конструктор копирования, этот временный объект должен будет привязаться к аргументу rhs конструктора копирования tester, который не является const ссылка. Но вы не можете связать временный объект со ссылкой не const, поэтому код не будет компилироваться.
Хотя вы можете обойти это, если хотите (просто не копируйте временный объект, а вместо этого привязайте его к ссылке const, которая продлевает время существования временного элемента до конца времени существования ссылки: const tester& my_t = f()), люди ожидают уметь копировать временные файлы вашего типа.

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


Вот еще один факт: в следующем стандарте C ++ вы можете перегрузить функции для временных объектов, так называемые rvalues. Таким образом, вы можете иметь специальный конструктор копирования, который принимает временные объекты, перегружающие «нормальный» конструктор копирования. Если у вас есть компилятор, который уже поддерживает эту новую функцию, вы можете попробовать его:

struct tester {
  tester(const tester&  rhs) { std::cout << "common copy ctor\n"; }
  tester(      tester&& rhs) { std::cout << "copy ctor for rvalues\n"; }
};

Когда вы используете приведенный выше код для вызова нашего f()

tester my_t = f();

новый конструктор копирования для rvalues ​​должен вызываться, когда временный объект, возвращаемый вызовом f(), копируется в my_t, и обычный конструктор копирования может быть вызван для копирования t объект изнутри f() в возвращенный временный объект. (Примечание: вам может потребоваться отключить оптимизацию вашего компилятора, чтобы увидеть это, поскольку компилятору разрешено оптимизировать все операции копирования.)
Так что вы можете с этим? Что ж, когда вы копируете значение r, вы знаете, что скопированный объект будет уничтожен после вызова конструктора копирования, поэтому конструктор копирования, принимающий значение r (T&&), может просто украсть значения из аргумента вместо их копирования. Поскольку объект все равно будет уничтожен, никто не заметит.
Для некоторых классов (например, для строковых классов) перемещение значения от одного объекта к другому может быть намного дешевле, чем копирование их.

4 голосов
/ 21 мая 2010

если я правильно понял ваш вопрос, чтобы избежать копирования / вызова конструктора объекта.

void function(const T&); // does not create new T
void function(T&); // does not create newT, however T must be modifiable (lvalue)
void function(T); // creates new T 

для простых типов создание новой копии тривиально (и часто оптимизируется компилятором). Для сложного объекта создание новой копии может быть очень дорогим. Следовательно, вы передаете его по ссылке.

https://isocpp.org/wiki/faq/references

https://isocpp.org/wiki/faq/ctors

если вы спрашиваете, почему не можете сделать следующее:

struct type {
  type(type);
};

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

вы можете сделать это, однако

struct type {
  type(type, int);
};

, поскольку этот конструктор отличается от синтезированного type(const type&)

http://en.wikipedia.org/wiki/Copy_constructor

2 голосов
/ 21 мая 2010

В дополнение к ответу @ aaa я постараюсь ответить на часть const. Часть const просто означает, что объект, который вы передаете логически, не изменяется. Это имеет смысл, потому что когда конструктор копирования вызывается с аргументом объекта Date d, d вообще не должен изменяться!

Вы можете удалить const, и ваш код все равно будет работать так же. Однако const обеспечивает дополнительную безопасность, так что вы никогда не сможете изменить переменную, помеченную как const. В вашем случае это означает, что вы не можете вызвать любой неконстантный метод Date. Это обеспечивается компилятором во время компиляции.

1 голос
/ 21 мая 2010

Исторически это является причиной введения ссылок на язык. Вот объяснение:

В C вы можете передавать значения в параметры по значению (void f(struct custom_type i)) или по указателю (void g(struct custom_type* i)).

Со значениями POD (int, char и т. Д.) Передача по значению не является проблемой, но если вы смотрите на сложные структуры, то стек растет слишком быстро, помещая целые структуры в стек для вызовов функций , Вот почему в Си вы склонны передавать структуры как параметры по указателю, даже если функция не изменяет их.

В C ++ есть случаи, когда ни один из вариантов не работал:

  • передача по указателям включает в себя нелогичный синтаксис для операторов (если вы определяете operator + для записи class custom_type, то custom_type a, b, c; a = &b + &c; будет нелогичным, поскольку a не присваивается сумма адресов. Кроме того, если вы хотите иметь возможность присваивать сумму значений a и сумму адресов a, вам придется как-то различать регистры по синтаксису).

  • передача по значению невозможна или нежелательна в случае конструкторов копирования. В вашем случае, если у вас есть Date(Date d) {} и присваивание Date a; Date b(a);, вы получаете то, что копия a создается просто для передачи в качестве параметра конструктору b. Это приводит к бесконечной рекурсии, поскольку создание копии a для передачи в качестве параметра аналогично Date d = a; b = Date(d);.

По этим причинам (и, возможно, были и другие) было принято решение создать references: типы данных, которые синтаксически выглядят как тип значения, но ведут себя как указатели (то есть указывают на значение другой переменной). , но вы обращаетесь к нему как к переменной, а не как к указателю).

Что касается причины, по которой вам нужно const в объявлении, это то, что ваш конструктор будет принимать временные объекты. Поскольку вы не можете изменить значение временных ссылок, если ваш конструктор не принимает const&, вы можете использовать конструктор копирования только для неконстантных стабильных объектов.

То есть, если у вас есть:

class Date
{
public:
    Date(Date& other); // non-const reference
    ...

Вы можете написать:

Date a;
Date b = a;

но не:

Date someFunction() { return Date(xxx); }
Date a = someFunction(); // someFunction returns a temporary object

ни

const Date someImportantDate;
Date a = someImportantDate; // cannot pass const value to non-const 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...