4

Empty我经常使用类似于以下的代码向我的 C++ 对象添加一个方法来清除内部状态。

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

这似乎比在构造函数中复制代码要好,但我想知道*this = Foo()在想要清除对象时是否是一种常用方法?这个等着咬我有什么问题吗?还有其他更好的方法来实现这种事情吗?

4

10 回答 10

11

我会让构造函数调用我的函数:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

是的,您首先不必要地将字符串初始化为空字符串,然后进行赋值,但这要清楚得多。

于 2009-01-12T17:42:06.993 回答
9

潜在问题?你怎么知道 *this 真的是 Foo?

于 2009-01-12T18:02:29.637 回答
6

您使用此 Empty 方法所做的工作本质上与手动将新构造的对象分配给变量(Empty 函数所做的事情)相同。

就个人而言,我会删除 Empty 方法,并将该方法的所有用法替换为:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

我真的认为使用这种 Empty 方法没有任何优势。它隐藏了它被称为女巫的对象的真实情况,这个名字也不是最好的选择。

如果调用者真的想要一个干净的对象 - 他自己构造对象将没有问题。

于 2009-01-12T18:30:26.600 回答
5

此外,考虑使对象不可变,,在构造时,它们不能更改。在很多情况下,这可以使您免受意料之外的副作用。

于 2009-01-12T17:56:33.987 回答
4

有一些比你建议的更常见的东西。使用交换。

基本上你做这样的事情:

T().swap(*this);

由于许多标准容器(所有 STL 容器?)都有一个恒定时间交换方法,这是一种清除容器并确保释放其存储空间的好方法。

同样,它也是“缩小以适应”容器的好方法,但使用复制构造函数而不是默认构造函数。

于 2009-01-12T18:24:03.273 回答
3

考虑使用放置new

void Empty() {
    this->~Foo();
    new (this) Foo();
}

您的代码调用operator =可能会导致各种副作用。

编辑以回应评论。– 该代码绝对是明确定义的,标准明确允许它。如果我有时间,我将在稍后发布该段落。关于delete——当然。我的意思是~Foo(),这是一个疏忽。是的,Rob 也是对的。破坏对象实际上是调用字符串的析构函数的必要条件。

于 2009-01-12T17:56:55.263 回答
2

如果您在构造函数中有动态分配的内存,这可能是内存泄漏的潜在来源。

于 2009-01-12T23:39:12.890 回答
2

这是我的做法:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

将交换函数放入与 Foo 类相同的命名空间中。当用户调用 swap 以交换两个 Foo 时,依赖于参数的查找会找到它。您也可以operator=使用您的交换功能来实现。

于 2009-01-13T13:58:00.533 回答
1

这似乎比在构造函数中复制代码要好,但我想知道 *this = Foo() 是否是想要清除对象时的常用方法?

清除一个对象并不常见:更常见的是,一个对象(甚至可能是一个不可变对象)被实例化并包含真实数据,或者它没有被实例化。

最常见的重置类型容器……但是,您现在不会编写自己的容器类,对吧。

这个等着咬我有什么问题吗?

是的:

  • 如果这不是真的 aFoo而是 aDerivedFoo
  • 如果Foo的赋值运算符不存在,或者它有问题(例如,如果它没有定义并且默认运算符不好,例如因为数据成员是裸指针)。

还有其他更好的方法来实现这种事情吗?

是的,也许免费功能会更好(可以避免上述两个问题):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}
于 2009-01-12T19:52:26.577 回答
-2

是的,这在性能方面效率不高(创建另一个 foo 对象而不是在原地工作),如果你在构造函数中分配内存并导致严重的内存泄漏,它会咬你一口。

使其在内存方面更安全将调用 this->delete 和 this = new foo() - 但它会很慢。

如果您想超快,请创建一个静态空对象字段并在 Reset 中对其进行 memcpy。

如果您只想快速地一一分配属性。

如果您想保持合理的风格而不重复,请按照 Ates Goral 的建议从 Ctor 调用 Reset,但是使用默认参数您将失去更快的构造。

于 2009-01-12T17:57:58.007 回答