如果未提供默认构造函数,我该如何使用流提取运算符?

How do I use a stream extraction operator if a default constructor isn't provided?

我有一个与 C++、构造函数(默认和其他)、流提取运算符和引用成员变量相关的软件工程问题。我知道有很多话题,但在这个问题上似乎所有话题都相互依赖。

对于我试图解决的问题,我想制作一个 Vec class 来保存问题的数据,以及一个 ProblemSolver class最终会有解决问题的方法。 ProblemSolver class 没有数据就没有意义,所以我用 Vec 数据的副本写了它(参考成员的考虑后来发生了......)。所以这就是我的第一个版本的样子:

#include <iostream>
#include <vector>

class Vec {
private:
    std::vector<int> v;
public:
    Vec(const int s) : v(s, 0) { }
};

class ProblemSolver {
private:
    Vec data;
public:
    ProblemSolver(Vec d) : data(d) { }
};

int main() {
    int m;
    std::cin >> m;
    Vec v(m);
    ProblemSolver ps(v);
}

到目前为止这似乎没问题。然后我认为我应该为每个 class 提供流提取运算符,以便对象可以负责读取自己的数据而不是在 main 中读取它。所以我重写了 classes 这样的:

#include <iostream>
#include <vector>

class Vec {
private:
    std::vector<int> v;
public:
    Vec(const int s) : v(s, 0) { }
    friend std::istream& operator >> ( std::istream& input, Vec &V ) {
        int m;
        input >> m;
        V.v = std::vector<int>(m, 0);
        return input;
    }
};

class ProblemSolver {
private:
    Vec data;
public:
    ProblemSolver(Vec d) : data(d) { }
    friend std::istream& operator >> ( std::istream& input, ProblemSolver &P ) {
        input >> P.data;
        return input;
    }
};

int main() {
    ProblemSolver p;
    std::cin >> p;
}

这是我 运行 感到困惑的地方,因为 main() 的第一行试图调用 ProblemSolver 的默认构造函数。我知道这是一个问题,因为没有提供,但就上述问题的良好做法而言,我真的应该怎么做呢?我是否应该始终提供默认构造函数,即使 class 在没有实际数据集的情况下没有意义?我应该只让对象处理自己的数据读取,而不应该编写流提取运算符,我错了吗?

由于 this article,我考虑使用引用成员变量来代替,因为“问题求解器的数据 class 仍然存在于问题求解器的实例之外”。所以我又这样重写了它:

#include <iostream>
#include <vector>

class Vec {
private:
    std::vector<int> v;
public:
    Vec(const int s) : v(s, 0) { }
    friend std::istream& operator >> ( std::istream& input, Vec &V ) {
        int m;
        std::cin >> m;
        V.v = std::vector<int>(m, 0);
        return input;
    }
};

class ProblemSolver {
private:
    const Vec& v;
public:
    ProblemSolver(const Vec& v_) : v(v_) { }
};

int main() {
    int m;
    std::cin >> m;
    Vec v(m);
    ProblemSolver p(v);
}

但是有了这个,我仍然无法为 ProblemSolver 编写流提取运算符,而为 Vec 编写的流提取运算符实际上是无用的,因为我必须实例化一个 Vec 对象,然后我才能使用流提取运算符。因此,在上面的示例中,我仍然必须读取 main() 中的 int m 数据,并且出于与第二个代码段中相同的原因,我无法执行类似 Vec v; std::cin >> v; ProblemSolver p(v); 的操作。我认为应该可以像 std::cin >> ProblemSolver p; 那样同时执行实例化和读取变量之类的操作,但显然情况并非如此。

所以我的主要问题是如何正确编写这两个 classes?我应该包括默认构造函数吗?我的理解是,如果 class 在没有任何数据的情况下没有意义,我不应该提供默认构造函数,但这是错误的吗?如果我不编写默认构造函数,我是否应该使用提供给标准构造函数的无用数据实例化一个 class,以便我可以在其上使用流提取运算符?在流提取之前错误地实例化对象似乎是一种不好的做法。

需要说明的是,我不只是在寻找可行的解决方案。我想为这些情况开发实际的良好实践和技术,所以请支持你的回答。

在这种情况下,必须先构造对象。

一些选项是:

  • 有一个默认构造函数,它用默认值(可能是 0)构造。

  • 有一个引用 istream 的构造函数,然后将使用它来加载值。

像这样:

class Vec {
private:
    std::vector<int> v;
public:
    Vec(const int s) : v(s, 0) { }
    Vec(std::istream& input) 
    {
        int m;
        input >> m;
        v = std::vector<int>(m, 0);
    }
    friend std::istream& operator >> ( std::istream& input, Vec &V ) {
        int m;
        input >> m;
        V.v = std::vector<int>(m, 0);
        return input;
    }
};


class ProblemSolver {
private:
    Vec v;
public:
    ProblemSolver(Vec v_) : v(v_) { }
    ProblemSolver() : v(0) { }
    ProblemSolver(std::istream& in) : v(in) { } 
};

如果您坚信 class 没有值就没有意义,那么您最好编写一个生成器函数并放弃流提取运算符。像这样:


class Vec {
private:
    std::vector<int> v;
public:
    Vec(const int s) : v(s, 0) { }
};

class ProblemSolver {
private:
    Vec data;
public:
    ProblemSolver(Vec d) : data(d) { }
    static ProblemSolver generate()
    {
        int m;
        std::cin >> m;
        return ProblemSolver{Vec{m}};
    }
};

int main() {
    ProblemSolver p = ProblemSolver::generate();
}