【问题标题】:Constructor initialization with arguments带参数的构造函数初始化
【发布时间】:2013-03-22 23:32:22
【问题描述】:

阅读 Andrew Koenig 和 Barbara E. Moo 的“Accelerated C++”的另一个问题,我在关于构造函数 (5.1) 的章节中,使用示例为 before

他们写

我们要定义两个构造函数:第一个构造函数不带参数并创建一个空的Student_info 对象;第二个引用输入流并通过从该流中读取学生记录来初始化对象。

导致使用Student_info::Student_info(istream& is) {read(is);}作为第二个构造函数的例子

将真正的工作委托给 read 函数。 [...] read 立即赋予这些变量新的值。

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 类下的函数,为什么需要使用第二个构造函数 - 这不是双重工作吗?为什么不只使用默认构造函数,然后使用函数,因为两者都已经定义了?

【问题讨论】:

    标签: c++ class constructor


    【解决方案1】:

    它不是双重工作,相反它简化了实例化类的调用者的对象初始化

    如果你每次都用一个构造函数来创建类

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

    也许可以添加一些其他可以在构造函数中完成的操作。所以当你需要实例化类时,你不必再次编写它们。如果您创建 10 个实例,则必须编写

    ( 10 -1 = ) 再多 9 行,这对于 OOP 来说不是一个好方法

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

    但是当你像上面这样定义两个构造函数时,你可以像这样使用类

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

    OOP 的主要目标之一是编写可重用而非自我重复的代码,另一个目标是seperation of concerns。在许多情况下,实例化对象的人不需要知道类的实现细节。

    在您的示例中,read 函数在构造函数内部调用时可以是私有的。这给我们带来了另一个面向对象的概念Encapsulation

    最后,这不是双重工作,它是软件设计的好方法

    【讨论】:

      【解决方案2】:

      我的问题是,既然 read 已经被定义为 Student_info 类下的一个函数,为什么还需要使用第二个构造函数——这不是双重工作吗?

      第二个构造函数从调用者那里获取一个参数并将其传递给read 函数。这允许您使用std::istream 直接实例化Student_info

      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 不必担心对象已“初始化”或有效。

      【讨论】:

      • 所以它保存了将所有变量初始化为(它们的版本为零)的步骤,就在它被覆盖之前?是否有任何速度/功率差异,或者只是为了清楚起见?
      • @sccs 无论如何都会首先初始化变量,因为该函数是从构造函数的主体中调用的。在这个阶段,所有的数据成员都已经构建好了。所以真正的原因是我在上一段中提到的设计方面。这不仅仅是清晰度:在构造之后必须显式初始化的类型非常无用(尽管我每天在工作中都会遇到它们)
      • 我刚刚看到了,谢谢!还有一个 - 是否存在不存在这样的第二个构造函数(带参数)的程序?
      • @sccs 你的意思是类?是的,有点。在构建阶段不需要任何外部输入的任何东西。但请记住,编译器会为您生成一个复制构造函数,这是一个带参数的构造函数。如果您不想这样做,则必须主动禁用它(但这是另一回事)。
      • 还有一个小补充:如果你的类在未初始化状态下是无用的,你不应该定义默认构造函数来更好地澄清它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      相关资源
      最近更新 更多