C++ 应该是C++;对象是否始终处于有效状态?

C++ 应该是C++;对象是否始终处于有效状态?,c++,oop,C++,Oop,每当构造对象时,构造函数是否应始终将其保持在“初始化”状态 例如,如果一个图像类有两个构造函数,其中一个接受文件路径字符串,另一个不接受参数,那么后者将图像对象保持在无效状态是否是一种不好的做法 假设该类是为处理这两种状态而编写的 我这样问是因为我发现在很多情况下几乎有必要使用默认构造。特别是当一个对象是一个类的成员,并且您希望在该类的构造函数中初始化它时 编辑:我知道成员初始化列表。我发现了一些情况,我希望在类的构造函数中构造对象,而不是之前。尽管如此,我知道这可能比任何其他替代方案都更危险。

每当构造对象时,构造函数是否应始终将其保持在“初始化”状态

例如,如果一个图像类有两个构造函数,其中一个接受文件路径字符串,另一个不接受参数,那么后者将图像对象保持在无效状态是否是一种不好的做法

假设该类是为处理这两种状态而编写的

我这样问是因为我发现在很多情况下几乎有必要使用默认构造。特别是当一个对象是一个类的成员,并且您希望在该类的构造函数中初始化它时


编辑:我知道成员初始化列表。我发现了一些情况,我希望在类的构造函数中构造对象,而不是之前。尽管如此,我知道这可能比任何其他替代方案都更危险。

这一切归结为“有效状态”的定义:如果类的方法在路径为空时处理状态,则具有空路径的状态是有效状态,并且绝对可以接受

但是,从编码的角度来看,这可能不是最优的,因为可能需要添加多个检查以确保路径有效。您通常可以通过实现来管理复杂性

我发现在很多情况下几乎有必要使用默认构造。特别是当一个对象是一个类的成员,并且您希望在该类的构造函数中初始化它时


只要在初始化列表中构造依赖项,就不需要默认构造函数来初始化它所属类的构造函数中的对象。

是的,它应该始终有效。但是,通常没有很好地定义是什么使对象有效。至少,对象应该在不崩溃的情况下可用。然而,这并不意味着所有操作都可以在对象上执行,但至少应该有一个操作。在许多情况下,这只是来自另一个来源(例如std容器迭代器)的
赋值
销毁
(即使在移动之后,这也是必须的)。但是,对象在任何状态下支持的操作越多,就越不容易出错


这通常是一种权衡。如果你能摆脱对象只有所有操作都有效的状态,那当然很好。然而,这种情况很少见,如果你必须跳越障碍才能达到目的,通常只需在一些功能中添加和记录前提条件就更容易了。在某些情况下,您甚至可以拆分接口,以区分进行此权衡的函数和不进行此权衡的函数。一个流行的例子是在
std::vector
中,您需要有足够的元素作为使用
运算符[]
的先决条件。另一方面,
at()
函数仍将工作,但会引发异常。

正常情况下,是的。我看到了一些很好的反例,但它们非常罕见。

你的最后一行:

我这样问是因为我发现在很多情况下几乎有必要使用默认构造。特别是当一个对象是一个类的成员,并且您希望在该类的构造函数中初始化它时

表示您没有使用成员初始值设定项列表。在这种情况下,您不需要默认构造函数。例如:

class Member {
public:
  Member(std::string str) { std::cout << str << std::endl; }
};

class Foo {
public:
  Foo() : member_("Foo") {}
private:
  Member member_;
}
类成员{
公众:

成员(std::string str){std::cout首先,让我们定义“有效状态”到底是什么:它是对象可以执行其工作的状态。
例如,如果我们正在编写一个管理文件的类,并允许我们写入和读取该文件,则有效状态(根据我们的定义)可能是对象持有正确打开的文件并准备在其上读取/写入的状态

但是考虑其他情况:什么是移动的价值的状态?< /P>

File::File( File&& other )
{
    _file_handle = other._file_handle;
    other._file_handle = nullptr; //Whats this state?
} 
这是一种文件对象尚未准备好对文件进行写入/读取,但已准备好进行初始化的状态。也就是说,这是一种准备好进行初始化的状态

使用复制和替换习惯用法考虑上述cTor的另一种实现方式:

File::File() :
    _file_handle{ nullptr }
{} 

File::File( File&& other ) : File() //Set the object to a ready to initialice state
{
    using std::swap; //Enable ADL

    swap( *this , other );
}
在这里,我们使用默认的ctor将对象置于准备初始化状态,只需将传递的右值与该对象交换,从而产生与第一个实现完全相同的行为

正如我们上面所看到的,一种是准备工作状态,一种对象准备好做应该做的事情的状态,另一种完全不同的是准备初始化状态:一种对象没有准备好工作,但准备好初始化并设置好工作的状态。


我对你的问题的回答是:一个对象并不总是处于有效状态(它不总是可以使用),但如果它还没有准备好使用,它应该准备好初始化,然后准备好工作。

是的,通常应该准备好,但是在某些情况下它是不可能或不可行的。关于您的问题:在这种情况下,成员初始化列表是存在的。如果您定义了一个,您的成员将不再是默认构造的(仅当该成员出现在ofc列表中时)考虑使用静态工厂方法进行构造,这样您可以返回无效的对象,比如“代码> Boo::可选的”。在这种情况下,图像类应该包含图像的加载吗?是图像类的路径部分吗?是的,这就是我在该示例中所想的。加载应该是图像类的一部分。o处理两种状态,则两种状态都无效。也许其中一种状态不如另一种状态有用,但仍然无效。但是,如果我想在构造函数中完成工作,以便初始化该对象,我将如何处理此问题?@Ben如果可能,制作一个私有静态函数来完成您计划在co中完成的“工作”
File::File() :
    _file_handle{ nullptr }
{} 

File::File( File&& other ) : File() //Set the object to a ready to initialice state
{
    using std::swap; //Enable ADL

    swap( *this , other );
}