Список инициализаторов конструктора в C++

Пожалуйста, сначала посмотрите код следующим образом:

class StrBlob
{
    public:
        StrBlob();
        // ....
    private:
        std::shared_ptr<std::vector<std::string> > data;
}
//initializer empty into data ;

StrBlob::StrBlob()
// : data(std::make_shared<std::vector<std::string> >())      // compile success
{
 //  data(std::make_shared<std::vector<std::string> >());     // compile error
}
int main()
{
    // this statement can compile
    std::shared_ptr<std::vector<std::string> >data(std::make_shared<std::vector<std::string> >());
    return 0;
}

Я хочу знать, почему при компиляции приведенного выше оператора возникает ошибка? ?

error: no match for call to ‘(std::shared_ptr<std::vector<std::__cxx11::basic_string<char> > >)
   (std::shared_ptr<std::vector<std::__cxx11::basic_string<char> > >)’
   data(std::make_shared<std::vector<std::string> >());

соответствующие знания, приведенные в C++Primer 5th (глава 7.5), выглядят следующим образом:

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

Сначала я поделюсь своей мыслью. Элемент данных по умолчанию инициализируется до того, как тело конструктора начнет выполняться. Правильно? Итак, элемент данных внутри конструктора скопирует объект, созданный из функции make_shared.правильно?


person Andy Cong    schedule 25.10.2016    source источник
comment
Потому что вы не можете конструировать/инициализировать такие объекты внутри тела конструктора. Это разрешено только в списке инициализаторов. Однако вы можете назначить уже созданные объекты внутри тела.   -  person Some programmer dude    schedule 25.10.2016
comment
Тем не менее, вы можете присваивать значения уже созданным объектам внутри тела Но вы не должны превращать это в привычку в коде C++.   -  person StoryTeller - Unslander Monica    schedule 25.10.2016


Ответы (2)


Первый успешно компилируется, потому что вы инициализируете данные в списке инициализаторов элементов, и такой синтаксис здесь вполне допустим. Проверьте это для получения дополнительной информации.

Второй равен data(...). Это не построение объекта data, компилятор видит это как попытку вызова operator() для уже созданного члена data ( и об этом написано в сообщении об ошибке).

Наконец, внутри функции main вы просто используете конструктор копирования shared_ptr для создания данных из shared_ptr, возвращаемых make_shared.

person Edgar Rokjān    schedule 25.10.2016
comment
вы правы. второй data(..) вызывает конструктор копирования? но объект данных уже создан до вызова данных (..). если так. это означает, что конструктор копирования вызывается дважды. при компиляции будет ошибка. я правильно говорю? - person Andy Cong; 25.10.2016
comment
Нет, второй не является вызовом конструктора копирования. Это вызов operator(), который не перегружен для вашего класса. Вот почему он не компилируется. - person Edgar Rokjān; 25.10.2016
comment
Я думаю некоторое время только сейчас, прежде чем вы не согласитесь с моей мыслью. Пытаюсь перегрузить член operator(). и это работает. соответствующие ошибки, данные компилятором, я думаю, что знаю это. С наилучшими пожеланиями ! Наконец, я хочу спросить, может ли это быть ошибкой из-за двойного вызова конструктора копирования, просто компилятор не сообщает об ошибке ?? - person Andy Cong; 25.10.2016
comment
@AndyCong Я не понимаю, как можно дважды вызывать конструктор копирования. Всего один вызов в списке инициализаторов членов... - person Edgar Rokjān; 25.10.2016
comment
Может быть, это ошибки моих взглядов. Я так много об этом думаю. поскольку действие data(..) похоже на конструктор копирования, я думаю, что проблема может упростить такой код следующим образом: std::vector‹int› vec1,vec2; век1(век2); а затем второе утверждение - это как раз проблема, о которой мы говорим. кажется, что действие конструктора копирования? ты думаешь ? Мне нужна ваша помощь. - person Andy Cong; 26.10.2016
comment
@AndyCong vec1(vec2) не вызывает конструктор копирования, vec1 уже создан в этот момент. Пожалуйста, прочитайте что-нибудь базовое о конструкторах копирования и создании объектов, ваш вопрос действительно базовый :) - person Edgar Rokjān; 26.10.2016
comment
как я только что сказал, потому что vec1 уже создан. поэтому предположим, что vec1 (vec2) является конструктором копирования в данный момент (мы игнорируем другую ошибку). также произойдет ошибка. потому что он вызывает конструктор копирования для создаваемого объекта. вот что я имею в виду. Я просто говорю о причине, по которой это может произойти. Я знаю, что не стоит об этом говорить. в любом случае. С наилучшими пожеланиями. - person Andy Cong; 26.10.2016

Если вы хотите инициализировать объект данных позже по многим причинам, вы можете использовать конструктор по умолчанию std::shared_ptr в списке инициализации StrBlob, а в теле конструктора StrBlob - reset функция:

StrBlob::StrBlob()
 : data()
{
   data.reset(new std::vector<std::string>());
}
person foxfireee    schedule 25.10.2016
comment
Я полагаю, что нам следует избегать таких конструкций, потому что этот код не является оптимальным. См., например, книги Мейерса. - person Edgar Rokjān; 25.10.2016
comment
@EdgarRokyan Это простой пример. На практике вы не всегда можете инициализировать объект в списке инициализации, потому что иногда вам нужно вычислить некоторые входные параметры. Что касается эффективности, я просто вызываю конструктор по умолчанию и функцию сброса. Я создаю векторный объект один раз. Я не подвергаю сомнению ваш ответ. На самом деле, я согласен с этим. Целью моего поста было показать другое решение. - person foxfireee; 25.10.2016