【问题标题】:Is it a good practice to make a method which modifies data outside the class const?制作一个修改类 const 之外的数据的方法是一种好习惯吗?
【发布时间】:2023-04-06 07:25:01
【问题描述】:

我想问一个关于方法的 const 正确性的问题。让我来说明一下情况。

class MyClass
{
public:
    ...
    void DiscussedMethod() { otherClass->NonConstMethod(); }

private:
    OtherClass *otherClass;
};

我有一个类MyClass,它保存了一个指向OtherClass 的指针。在DiscussedMethod 中,它调用OtherClass::NonConstMethod 来修改一些可见数据。

我想知道,制作DiscussedMethod const 是否是一个好习惯(因为它不会修改任何成员数据)?这会是一个坏习惯吗?还是两者都可以?

如果OtherClass 保留了指向MyClass 的指针,并且在NonConstMethod 中修改了MyClass 的一些数据(这意味着MyClass 成员数据将在DiscussedMethod 调用期间发生变化),那会怎样。那么制作DiscussedMethod const 会是一种不好的做法吗?

据我所知,方法上的 const 主要是代码文档,所以我可能倾向于不制作 DiscussedMethod const,但我想听听你的意见。

编辑:有些回复会考虑otherClass 指向的对象是否归MyClass 对象所有。在我正在使用的场景中,情况并非如此。可以说两个对象并排独立存在(具有相互修改的能力)。我认为这个类比很好地描述了我的情况。

例如考虑像双向链表这样的东西,其中每个元素都是一个类,它保存指向其邻居的指针和成员变量color。它有方法MakeNeighboursRed,它改变了它的邻居的color,但不影响调用对象本身的状态。我是否应该考虑使用这种方法const

如果MakeNeighboursRed 有可能会调用邻居的MakeNeighboursRed 会怎样。所以最终,最初调用MakeNeighboursRed 的对象的状态也会改变。

我要感谢大家的意见:-)

【问题讨论】:

  • 这只是我个人的看法。有时指向的对象我认为是“主要对象的一部分”。这意味着,我可能会让OtherClass 成为正式成员,但它需要是动态/多态/等。在这种情况下,我不会将函数设为 const,即使它可能是。
  • 关于你的编辑:我坚持我的回答:如果调用一个方法可能会改变你的对象的状态,那么它不应该是 const。 (即你改变了可能会改变这个状态的邻居。)我希望一个 const 方法来保留对象的状态。

标签: c++ methods constants


【解决方案1】:

如果MyClass 拥有OtherClass 实例,我不会将DiscussedMethod 设为常量。

管理资源的类也是如此。 IE。标准容器不会使用 const 函数返回非 const 引用或指向托管内存的指针,尽管它是“可能的”(因为没有修改保存资源的实际指针)。

考虑

class MyClass
{
public:
    bool a() const { return otherClass->SomeMethod(); }
    void b() const { otherClass->NonConstMethod(); }
private:
    OtherClass *otherClass;
};

void foo (MyClass const &x)
{
    cout << boolalpha << x.a() << endl;
    x.b(); // possible if b is a const function
    cout << boolalpha << x.a() << endl;
}

foo 可以打印两个不同的值,尽管foo 的实现者可能希望对 const 对象的两个函数调用具有相同的行为。

澄清:

根据标准,以下是无效的,因为operator[]的const版本返回std::vector&lt;T&gt;::const_reference,这是一个对值类型的常量引用。

std::vector<int> const a = { /* ... */ };
a[0] = 23; // impossible, the content is part of the state of a

如果这个函数只有一个签名,即referece operator[] (size_t i) const;,这是可能的,因为该操作不会改变向量的内部指针,而是它们指向的内存。

但是由向量管理的内存被认为是向量状态的一部分,因此无法通过 const 向量接口进行修改。

如果向量包含指针,则这些指针仍然无法通过公共 const 向量接口进行修改,尽管存储在向量中的指针很可能是非 const 的,并且很可能会更改它们指向的内存。

std::vector<int*> const b = { /* ... */ };
int x(2);
b[0] = &x; // impossible, b is const
*b[0] = x; // possible since value_type is int* not int const *

【讨论】:

  • "the standard containers do not return non const references or pointers to the managed memory using const functions" 请详细说明,并考虑const vector&lt;int*&gt; v = {..}; *v[0] = 2;
  • @NeilKirk :在您的示例中,v[0] 返回对指向 int 的非常量指针的常量引用。 *v[0]=2; 不会更改指针,而是更改指针指向的内存。 (v[0] = &amp;some_int; 是不可能的)本质上它表现出与问题本身相同的“问题”/行为(T * constT * const * 不同。)
  • otherClass-&gt;NonConstMethod(); 不会修改指针,但它指向的内存(可能)和 operator[] 在这种情况下是 const。
  • @NeilKirk 是的,但 vector&lt;int*&gt; 拥有的指针不是 int* 而是(可能)向量的 int** 成员。这相当于问题中的OtherClass* 成员。并且成员函数operator[] 的 const 版本不会改变向量成员指向的内存。您的示例多了一层间接性。
【解决方案2】:

在 OOP 中,对象应由其状态完整描述,可通过其接口获得。因此,如果可以通过接口观察到这些变化,则 const 方法不应改变对象的状态。

一个很好的例子是在你的类中使用mutable 互斥锁来保护一些共享资源。它可能会从const 方法修改,因为它不会引入任何可通过类接口观察到的变化。

【讨论】:

    【解决方案3】:

    一般的经验法则是,如果您可以将成员函数设为 const,那么您可能应该这样做。这样做的原因是它可以让您更轻松地捕获意外行为和错误。

    另一个赞成的论点是,如果你有这个函数作为 const,你可以在 const 对象上调用它,所以它不是真正的文档。

    【讨论】:

      【解决方案4】:

      总的来说,这取决于其他类是什么。不是非黑即白……

      如果otherClass 是一个日志对象(例如),并且您想记录当前对象的操作,那么从 const 函数调用它是完全可以的。

      如果 otherClass 是一个容器,出于设计(或实现)目的被实现为单独的对象,而不是 const 函数有效地修改对象,这是一个非常糟糕的主意。

      我希望这会有所帮助。

      【讨论】:

        【解决方案5】:

        DiscussedMethod const 设置为完全不正确,因为它会更改为*this 状态。唯一的漏洞是使 non-logically-part-of-object's-state 成员数据 mutable,因此可以在 const 函数中更改它们。这就像一个成员持有“函数x() 被调用的次数”的计数。其他任何东西都是对象的状态的一部分,如果一个函数改变它(在任何级别),那个函数就不是const

        【讨论】:

        • 完全不正确是什么意思?它可以编译,所以它必须有点正确*。就编译器而言,它在技术上并未修改*this。 (* 笑话)
        【解决方案6】:

        我想知道,将 DiscusedMethod 设为 const 是否是一个好习惯(因为它不会修改任何成员数据)?

        otherClass 成员数据,它(或者更确切地说,它指向的对象)被修改了。

        如果将指向 otherClass 的指针重构为完全拥有的对象,请考虑语义...无论某物是作为指针、引用还是对象都不会改变语义所有权,IMO。

        【讨论】:

        • 我们没有足够的上下文来知道otherClass 是否具有完全拥有对象的语义。考虑const vector&lt;int*&gt; v = {..}; *v[0] = 2;
        猜你喜欢
        • 1970-01-01
        • 2017-06-14
        • 2017-08-09
        • 1970-01-01
        • 2016-12-19
        • 1970-01-01
        • 2010-09-11
        • 1970-01-01
        • 2012-05-07
        相关资源
        最近更新 更多