Учебник по C 5 — е издание (Стенли) — Вопрос по упражнению 7-39

#c #c 11

Вопрос:

Проработка некоторых упражнений в C Primer 5-е Изд (Стенли) и упражнение 7-39.

  1. Занятие по упражнению 7-39.
     class Sales_data {
    public:
    
    // defines the default constructor as well as one that takes a string argument
    Sales_data(std::string s = ""): bookNo(s) {}
    
    // remaining constructors unchanged
    Sales_data(std::string s, unsigned cnt, double rev): bookNo(s), units_sold(cnt), revenue(rev*cnt) {}
    
    Sales_data(std::istream amp;is) { read(is, *this); }
    
    // remaining members as before
     
  2. Вопрос:
    Упражнение 7.39: Было бы законно, чтобы как конструктор, принимающий
    строку, так и конструктор, принимающий потокamp;, имели аргументы по умолчанию? Если
    нет, то почему бы и нет?
  3. Мой ответ:
    Да. Я могу получить следующее:

      Sales_data(std::string s = ""): bookNo(s) {}
     Sales_data(std::istream amp;is = std::cin) { read(is, *this); }
     

Все вышесказанное справедливо.
Однако есть некоторые сайты с решением для этого 5-го издания C Primer, на котором написано «нет», как показано ниже:

https://github.com/Mooophy/Cpp-Primer/tree/master/ch07 (вам нужно прокрутить вниз до упражнения 7-39. Ответ не имеет смысла)

https://github.com/jaege/Cpp-Primer-5th-Exercises/blob/master/ch7/7.39.md (этот ответ вообще не имеет смысла)

Я хотел бы услышать другое мнение, если я что-то упускаю. Я все еще стою на своем ответе Фаруку.

Тнх

Комментарии:

1. Какой из них выбрал бы компилятор, если бы он не предоставлял аргументов?

Ответ №1:

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

Так что это бессмысленно

 Sales_data(std::string s = "") : bookNo(s) {}
Sales_data(std::istream amp;is = std::cin) { read(is, *this); }
 

и лучше писать так:

 Sales_data(std::string s) : bookNo(s) {}
Sales_data(std::istream amp;is) { read(is, *this); }
 

Комментарии:

1. Я думаю, что лучше обобщить ответ на неоднозначные конструкторы. Потому что они могут не быть конструкторами по умолчанию. Например , если конструкторы были похожи Sales_data(const std::stringamp;, int i = 0) на и Sales_data(const std::stringamp;, const std::stringamp; s = "") , мы не можем вызывать их снова с помощью одной строки.

2. @Afshin Также не нужно ограничивать его конструкторами . Это относится к любой функции.

3. правда 🙂 Я имел в виду то же самое, просто дал образец.

4. Аргумент по умолчанию НЕ является параметрами?

5. @yapkm01 Значение по умолчанию будет использоваться выбранным интерпретатором, если пользователь класса не предоставит его. Если вы не указываете значение, у вас есть два конструктора, одинаково подходящих для использования. Он может выбрать только один, но компилятору недостаточно информации для его выбора, поэтому произойдет ошибка компиляции.

Ответ №2:

Код может иметь более одного конструктора по умолчанию и при этом быть хорошо сформированным.

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

Например, рассмотрим что-то вроде этого:

 class foo {
    int x;
public:
    foo() : x(1) {}
    foo(int x = 0) {}
};
 

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

Но если вы попытаетесь по умолчанию создать объект такого типа:

 int main() { 
    foo f;
}
 

этот код плохо сформирован (т. е. правильно функционирующий компилятор должен отклонить его).

Тем не менее, большинство разумно здравомыслящих людей, просматривающих ваш код, обычно считают это плохой идеей. Когда кто-то видит подобный конструктор foo(int x=0) , он склонен предполагать, что цель состоит в том, чтобы вы могли создать объект этого класса по умолчанию. Но если у вас есть неоднозначные конструкторы, вы не можете, поэтому код в основном лжет.

Краткие сведения

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

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

Комментарии:

1. Короче говоря, аргумент по умолчанию НЕ является параметром, верно?

Ответ №3:

Нет, вы не можете иметь и то, и другое. Это приведет к ошибке компиляции C2668: 'Sales_data::Sales_data': ambiguous call to overloaded function при попытке построить объект без каких-либо аргументов, как показано в примере ниже.

 #include <string>
#include <iostream>

class Sales_data { 
    public: 
    Sales_data(std::string s = "") {
        std::cout << "1st constructor"  << std::endl;
    } 
    Sales_data(std::string s, unsigned cnt, double rev) {
        std::cout << "2nd constructor"  << std::endl;
    } 
    Sales_data(std::istream amp;is = std::cin) {
        std::cout << "3rd constructor"  << std::endl;
    }
};

int main() {
    Sales_data s;  // The compiler would fail here. :)
    return 0;
}
 

Обратите внимание, что определение Sales_data s; в основной функции неоднозначно, поскольку разработчик не знает, следует ли ему вызывать Sales_data(std::string s = "") или Sales_data(std::istream amp;is = std::cin) создавать объект.