2013-06-05 5 views
1

Допустит, мой файл выглядит следующим образом:стиль параметра конструктора

#include <iostream> 
#include <string> 

using namespace std; 

class testclass{ 
public: string name; 
     //testclass(const string& sref){ 
      // name = sref; 
     //} 
     testclass(string str){ 
      name = str; 
     } 
     ~testclass(){} 
}; 

int main(){ 
    testclass t1("constStringRef"); 
    cout << t1.name << '\n'; 
} 

Каковы различия между конструктором 1 и 2 с учетом следующего конструктора вызов:

testclass tobj("tmemberstring"); 

Вот что я думал :

Я знаю, что передача по ссылке означает, что вы не передаете копию, но из-за аргумента строки сначала начинается инициализация строки (в обоих случаях рассматривается как локальная переменная, i as sume), после чего следует инициализация ссылки на него в случае 1 или копия на новую строку str в случае 2. В конце оба из этих конструкторов копируют значения в имя строки-члена. Если мои мысли верны, я бы пропустил один шаг (копирование в строку str), если бы использовал первый конструктор.

Боковые вопросы: Аргументы хранятся в области стека? И если да, то сколько места занимает эта конкретная ссылка на строку или ссылка на какие-либо базовые типы данных?

Надеясь за ваши советы, заранее спасибо

+1

Все в вашем коде не имеет смысла. что такое 'n' и' s' ?! –

+0

Вы должны попытаться скомпилировать код перед раздуванием его на StackOverflow! –

+0

Извините, переименовал его, чтобы сделать его понятным и забыл переименовать задания. – TMK241

ответ

2

Самый простой способ ответить на ваш вопрос - сломать то, что происходит в обоих случаях.

testclass(const string& sref)

  • testclass t1("constStringRef"); сначала создает временный string объект из const char*
  • конструктор вызывается, временный string объект привязан к const string& аргумента конструктора
  • name является бесцельно по-умолчанию, так как вы не использовали список инициализаторов конструктора (подробнее об этом позже)
  • string::operator = называется, что делает копию const string& аргумента

Итого: 1 экземпляр.

testclass(string str)

  • testclass t1("constStringRef"); первый создает временный string объект из const char*
  • конструктор называется - то, что происходит, зависят от C++ версии вы используете:
    • C++ 03: временный объект string копируется в аргумент конструктора
    • C++ 11: временный перемещен в аргумент конструктора
  • name является бесцельно по-умолчанию, так как вы не использовали список инициализации конструктора
  • string::operator = называется, что делает копию string аргумента

Итого: 2 копии в C++ 03, 1 копия на C++ 11.


Исходя из этого, мы можем полагать, что const string& лучше. Однако это относится только к C++ 03.


C++ 11 и переместить семантика

В C++ 11, то лучше (в данном конкретном случае), чтобы передать строку по значению конструктора, а затем переместить аргумент в ваш член класса:

testclass(string str){ 
     name = std::move(str); 
    } 

Давайте посмотрим, что происходит сейчас:

  • testclass t1("constStringRef"); первый создает временный string объект из const char*
  • конструктор вызывается, временным это переехал в аргумент конструктора
  • name является бесцельно по-умолчанию, так как вы не использовали список инициализации конструктора
  • string::operator = называется, но на этот раз перемещениеstring аргумент в name

Итого: 0 коп у!


Это все нормально с rvalues, но сохраняет ли это значение для lvalues?

string s = "..."; // s has already been constructed some time ago 
testclass t1(s); // what happens during this call? 
  • для конструктора, который принимает const string& (как в C++, 03 и C++, 11):

    • s связан с const string& аргумента
    • name является бесполезно построенный по умолчанию, поскольку вы не использовали список инициализаторов конструктора
    • string::operator =, составление копии аргумента const string&
    • Всего: 1 копию.
  • для конструктора, который принимает string, а затем перемещает его (только в C++ 11):

    • s копируется в string аргумент
    • name является по умолчанию бесполезно построенного так как вы не использовали список инициализаторов конструктора
    • string::operator = вызывается, но на этот раз movingstring аргумент в name
    • Всего: 1 копию.

Обертывание его

В C++ 03, будь вы передаете именующее выражение или Rvalue не имеет значения, это всегда более эффективно использовать const string&. Как уже упоминалось, вы можете перегрузить ваш конструктор, чтобы принять аргумент const char* и тем самым избежать ненужной копии.

В C++ 11, если вы переместите аргумент в переменную-член, string аргумент такой же, как const string& аргумент для lvalues, но это является более эффективным для rvalues ​​(копия не должна быть выполнена в все). Поэтому вы должны использовать pass-by-value, а затем переместить аргумент в переменную-член.


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

// C++03 
    testclass(const char* str) : name(str) {}  // no copy 
    testclass(const string& sref) : name(sref) {} // 1 copy 

    // C++11 
    testclass(string str) : name(std::move(str)) {} // 1 copy for lvalues, 
                // no copy for rvalues 
+0

wow, что было потрясающе! большое спасибо – TMK241

1

В обоих случаях, конструктор принимает std::string. Поскольку вы вызываете конструктор со строковым литералом (const char*), временный std::string будет построен для вызова конструктора. Разница между этими двумя методами заключается в следующем:

В случае testclass(const string& sref) a const ссылка на временный string, который вы только что создали, может быть сделано. Во втором случае строка берется значением, поэтому необходимо создать второй временных потребностей.

  • примечание: компиляторы могут иногда оптимизировать этот второй временный проезд.

Как правило, я предлагаю использовать по возможности const&.

Однако обратите внимание, что вы могли бы избежать строительства временного std::string вообще, просто приняв строковых литералов с помощью шаблона:

template <size_t N> testclass(const char (&str)[N]) 
{ 
    name = str; 
} 

Заметим также, что, когда ваш конструктор вызывается две вещи. 1) Построен элемент name. 2) Изменяется значение члена name. Вы можете инициализировать и построить name элемент в одном шаге, используя список инициализации:

template <size_t N> testclass(const char (&str)[N]) 
: 
    name (str, N-1) // minus one to not copy the trailing `\0`. Optional, depending 
{ 
    name = str; 
} 
+0

Большое спасибо – TMK241

+0

'template testclass (const char * (& str) [N]) '=> Это совершенно неправильно. Здесь вы берете 'array [N]' 'const char *'. Либо возьмите только 'const char *', либо 'const char (& str) [N]'. Очевидно, что только 'const char *' предпочтительнее, потому что массивы могут распадаться на указатели, в то время как указатели не могут быть переведены на массивы фиксированной длины. Кроме того, почему инициализация + присваивание? – syam

+0

@syam: Спасибо, что указали это. Это то, что я получаю за не компиляцию. –

0

Прежде Алле, лучше закончить свои потоковые линии с «станд :: ENDL» не «„\ п“». Один конструктор ожидает ссылки, другой - значение, но вы передаете C-String, значение которого является «const char *». В первую очередь: инициализируйте свой член перед вызовом кода-конструктора. Я бы рекомендовал следующее:

testclass(const char* name) : name(name) {}; 
+0

'cout' обычно является строковым буфером, и в этом случае' endl' vs. '\ n' не имеет никакого значения. – Praetorian

+0

@Praetorian: Но все же, почему бы заставить флеш лучше советом? –

+0

Да, я знаю, что я прошел c-строку, но из-за конструктора преобразования строки это не имеет значения. исправьте меня, если я ошибаюсь, и спасибо за вашу информацию об инициализации. – TMK241

0

Я знаю, что передача по ссылке означает, что вы не передаете копию

Это верно, так что вы должны обычно предпочитают testclass::testclass(const std::string&), потому что он избегает, что копировать

, но из-за аргумента строки сначала начинается инициализация строки

Да, существует временная строка, созданная и переданная как const ref, или аргумент создается напрямую.

Есть еще одна строка: ваш член name по умолчанию инициализирован только потому, что вы не использовали список инициализаторов. Это:

testclass::testclass(const std::string &s) : name(s) {} 

инициализирует nameнепосредственно без первого по умолчанию, его инициализации, а затем изменить его в теле конструктора.


Sidequestions: Есть аргументы, хранящиеся в области стека?

аргументы могут быть помещены в стек или в регистры или любым другим способом, который обнаружит компилятор, соответствующий стандарту. Это деталь реализации.

... И если да, то сколько места будет занимать эта конкретная строка или ссылка на какие-либо базовые типы данных?

В общем, ссылка, вероятно, самое размер указателя (и может быть полностью опущены).

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

+0

Спасибо за совет! – TMK241