Инициализация .. какая из них более эффективна? - PullRequest
10 голосов
/ 12 марта 2009

У меня следующий вопрос. Какой из них лучше, которому следует следовать и почему?

string strMyString = "SampleString";

или

string strMyString("SampleString");

Заранее спасибо.

Ответы [ 4 ]

21 голосов
/ 12 марта 2009

я ответил здесь

В этот ответ я вставил одну вещь: Ни один из них не использует оператор присваивания .

Хотя краткое объяснение специфической для строки вещи. std::string имеет конструктор, принимающий один аргумент, который принимает char const*:

// simplified to a normal class declaration. std::string actually
// is a template instantiation. 
class string {
public:
    string(char const* str) {
        // copy over...
    }
};

Теперь вы видите, что у конструктора есть указатель на символ (ы). Так что он может принимать строковый литерал. Я думаю, что следующий случай очевиден:

string s("hello");

Он вызовет конструктор напрямую и тем самым инициализирует s. Это называется прямой инициализацией .

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

// uses copy initialization
string s = "hello";

Во-первых, давайте сформулируем типы

  • s имеет тип std :: string
  • "hello" - это массив, который в этом случае снова обрабатывается как указатель. Поэтому мы будем рассматривать его как char const*.

Компилятор ищет два способа сделать преобразование.

  • Есть ли в std :: string конструктор преобразования ?
  • Имеет ли инициализатор тип с функцией оператора преобразования, возвращающей std::string?

Он создаст временный std::string одним из тех способов, который затем используется для инициализации объекта s с помощью std::string копирующего конструктора . И он видит, что std::string имеет конструктор преобразования, который принимает инициализатор. Так что он использует это. В конце концов, это фактически так же, как

std::string s(std::string("hello"));

Обратите внимание, что форма, используемая в вашем примере, которая вызвала все это

std::string s = "hello";

определяет неявное преобразование . Вы можете пометить конструктор, принимающий char const* как явный для ваших типов, если вам интересно узнать о правилах инициализации вашего материала, и он не позволит использовать соответствующий конструктор в качестве конструктора преобразования больше:

class string {
public:
    explicit string(char const* str) {
        // copy over...
    }
};

При этом его инициализация с использованием copy initialization и char const* фактически запрещена (и в других местах)!

Так вот, это было, если компилятор не поддерживает удаление временных файлов в разных местах. Компилятору разрешается предполагать, что конструктор копирования копирует в этом контексте и может исключить дополнительную копию временной строки и вместо этого создать временную строку std :: string непосредственно в инициализированном объекте. Однако конструктор копирования должен быть доступен, в частности. Таким образом, инициализация копии будет недействительной, если вы сделаете это

class string {
public:
    explicit string(char const* str) {
        // copy over...
    }

private: // ugg can't call it. it's private!
    string(string const&);
};

Теперь на самом деле допустим только случай прямой инициализации.

7 голосов
/ 12 марта 2009

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

; 9    :    std::string f("Hello");

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _f$[esp+80]
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

; 10   :    std::string g = "Hello";

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _g$[esp+80]
    mov DWORD PTR __$EHRec$[esp+88], 0
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

... но это артефакт, потому что это был первый, который увидел компилятор. Измените код, поменяв местами заказ:

; 9    :    std::string g1 = "Hello";

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _g1$[esp+136]
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

; 10   :    std::string f1("Hello");

    push    OFFSET ??_C@_05COLMCDPH@Hello?$AA@
    lea ecx, DWORD PTR _f1$[esp+136]
    mov DWORD PTR __$EHRec$[esp+144], 0
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z

... и о чудо, секунда - это наставление на одну меньше.

Мы также видим, что этот компилятор (Microsoft VC ++ 2005, Настройки выпуска) генерировал один и тот же ассемблер для обеих версий. Так что в этом компиляторе нет разницы, и вы можете это доказать.

2 голосов
/ 12 марта 2009

Единственное реальное отличие состоит в том, что первый технически требует использования конструктора копирования, но компилятору разрешено исключать его, так что эффективность будет одинаковой в обоих случаях.

Однако, во-первых, требуется, чтобы конструктор копирования был доступен (т. Е. Не закрытым), даже если он фактически не используется.

1 голос
/ 12 марта 2009

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

Сам вопрос - забавный , потому что он помогает показать операции конструкторов и назначений C ++, но на практике, если вы тратите время на попытки оптимизировать это (а публикация на SO является достаточным доказательством Вы ..) Вы действительно наклоняетесь на ветряные мельницы.

Лучше избегать отвлечения и тратить свои усилия в другом месте.

...