2013-03-22 3 views
2

Еще один вопрос из чтения «Ускоренный C++» Эндрю Кенига и Барбары Э. Му, и я нахожусь в главе о конструкторах (5.1), используя пример как before.Инициализация конструктора с аргументами

Они пишут

мы хотим определить два конструктора: первый конструктор не принимает никаких аргументов и создает пустой Student_info объект; вторая берет ссылку на входной поток и инициализирует объект, читая студенческую запись из этого потока.

ведет к примеру с помощью Student_info::Student_info(istream& is) {read(is);} в качестве второго конструктора, который

Делегаты реальной работы к функции считывания. [...] чтение немедленно дает этим переменным новые значения.

Student_info класс

class Student_info { 
public: 
    std::string name() const (return n;} 
    bool valid() const {return !homework.empty();} 
    std::istream& read(std::istream&); 

    double grade() const; 
private: 
    std::string n; 
    double midterm, final; 
    std::vector<double> homework; 
}; 

read Поскольку уже определен как функция под Student_info класса, почему существует необходимость использовать второй конструктор - не такая двойная работа? Почему бы просто не использовать конструктор по умолчанию, а затем функцию, так как оба уже определены?

ответ

1

Вопрос, так как чтение уже определено как функция класса Student_info, почему нужно использовать второй конструктор - не эта двойная работа?

Второй конструктор принимает аргумент от вызывающего и передает его функции read. Это позволяет вам напрямую создавать код Student_info с std::istream.

std::istream is = ....; 
Student_info si(is); // internally calls read(is) 

в отличие от

std::istream is = ....; 
Student_info si; 
si.read(is);  // how could we use is if this call isn't made? Now we have to read some docs... 

Почему бы не просто использовать конструктор по умолчанию, а затем функция, так как уже определены?

Потому что лучше построить объекты так, чтобы они находились в согласованном, полезном состоянии, а не сначала строили их, а затем инициализировали. Это означает, что пользователям объектов не нужно беспокоиться о том, может ли эта вещь использоваться или должна быть сначала инициализирована. Например, эта функция принимает ссылку на Student_info:

void foo(const Student_into& si) 
{ 
    // we want to use si in a way that might require that it has been 
    // initialized with an istream 
    si.doSomethingInvolvingInputStream(); // Wait, what if the stream hasn't been read in? 
             // Now we need a means to check that! 
} 

В идеале foo не придется беспокоиться о том, что объект был «инициализирован» или сделаны действительными.

+0

Так что сохраняет шаг инициализации всех переменных в (их версии нуля) непосредственно перед ним записывается? Есть ли разница в скорости/мощности, или это просто для ясности? – sccs

+1

@sccs переменные сначала инициализируются, потому что функция вызывается из тела конструктора. На этом этапе все элементы данных были построены. Поэтому настоящая причина - это тот аспект дизайна, который я упомянул в последнем абзаце. Это не просто ясность: тип, который должен быть явно инициализирован после того, как строительство довольно бесполезно (хотя я сталкиваюсь с ними каждый день на работе) – juanchopanza

+0

Я только что увидел это, спасибо! Еще один - существуют ли когда-либо программы, в которых нет такого второго конструктора (с аргументами)? – sccs

5

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

Если вы создаете класс с одним конструктором каждый раз, когда вы должны сделать

std::istream is = std::cin; 
Student_info si(); 
si.read(is); 
// si.foo(); 
// si.bar(); 
// si.baz(); 

Возможно, некоторые другие операции могут быть добавлены в конструктор. Поэтому вам не нужно писать их снова, когда вам нужно создать экземпляр класса. При создании 10 экземпляров вам придется писать

(10 -1 =) больше 9 строк, и это не очень хороший подход к ООП

Student_info::Student_info(istream& is) 
{ 
    read(is); 
    //foo(); 
    //bar(); 
    //baz(); 
} 

Но когда вы определили два конструктора, как и выше, вы можно использовать класс как

std::istream is = std::cin; 
Student_info si(is); 

Одной из главных целей ООП писать повторно и не само повторение кода и другой целью является seperation of concerns. Во многих случаях человеку, который создает экземпляр объекта, не нужно знать детали реализации класса.

На вашем примере read функция может быть частной, когда ее вызываемый внутренний конструктор. Это доходит до нас другой концепции объектно-ориентированного программирования Encapsulation

Наконец это не двойная работа и ее хороший подход к разработке программного обеспечения

Смежные вопросы