【问题标题】:Are const class members useful when the assignment operator is overloaded?当赋值运算符重载时,const 类成员有用吗?
【发布时间】:2012-02-11 05:09:35
【问题描述】:

我有一个类的成员没有被类的方法改变,所以我将它标记为const。我的问题是我像复制构造函数一样使用默认赋值运算符,以避免多次声明。但是在这种情况下,赋值运算符不会自动生成,所以我得到了一些编译器错误: 'operator =' function is unavailable。这似乎在现实生活中没有可以实际使用 const 类成员的场景(例如,您在 STL 代码中见过任何 const 成员吗?)。

除了删除const 之外,还有什么方法可以解决这个问题?

编辑:一些代码

class A
{
public :
   const int size;
   A(const char* str) : size(strlen(str)) {}
   A() : size(0) {}
};


A create(const char* param)
{
    return A(param);
}


void myMethod()
{
    A a;

    a = create("abcdef");
    // do something

    a = create("xyz");
    // do something
}

【问题讨论】:

  • 我将标题编辑为我认为更贴切地描述了您要问的内容,并鼓励提供更有用的答案。如果您不同意,请随时更改。

标签: c++


【解决方案1】:

您不能拥有const 成员和支持任务,至少不能 具有预期语义的赋值。 const 在逻辑上是一个 承诺成员永远不会改变,分配是 (隐含地,在大多数人的脑海中)承诺所有数据 成员将采用右侧成员的值 (这通常意味着改变)。有一个非常明确的冲突 在这两个承诺之间。

当然,很多类型一开始就不应该支持赋值; 对于不支持赋值的类型,声明一个没有问题 数据成员const。然而,总的来说,我发现const 很多 在这里用处不大; const 是合同的一部分,数据成员是 通常不是类的外部合同的一部分。 (但很多 取决于——如果数据成员是公开的或受保护的,那么事实 它是不可变的可能是外部合同的一部分。和的 当然,表达内部类不变量并没有错 在语言结构中也是如此。)

【讨论】:

  • “你不能”——嗯,你可以,但是你必须定义一个自定义分配。例如,由 X 分配的逻辑可分配对象可能包含指向 X 的 const 指针,以帮助要求 X 释放该对象。例如,这样的事情可能会解决现有框架带来的一些问题。指针将在对象的生命周期内保持不变,并且不会作为逻辑对象状态的一部分公开。 IE。它不会是外部可分配状态的一部分。干杯&hth.,
【解决方案2】:

const 成员在很多很多情况下都是理想的。当然,有一个明显的情况是值不应该或不能改变,但这也是优化和并发的一个重要限制——不是每个类型都需要或应该有一个赋值运算符。

如果成员需要赋值行为,那么变量一定不能是const

当值/成员不能变异或被this 变异时,为变量成员(甚至更复杂情况下的子类型->组合)提供单独的接口会更清晰:

class t_text {
public:
// ...
public:
    void setString(const std::string& p);
private:
    const t_text_attributes d_attributes;
    std::string d_string;
};

因此,为了清晰起见,我的建议是隐藏赋值运算符,并使“可变块”或成员可设置:

text.setString("TEXT"); // << Good: What you read is what happens.
text = otherText; // << Bad: Surprise - attributes don't actually change!

【讨论】:

  • 优化和并发是我能想到的使用const的最不重要的原因。我会说,通过强迫你思考(或者至少强迫你为不得不抛弃一贯性而感到有点内疚)来防止你做愚蠢的事情更为重要。
  • @Frerich 也许这是我的错误,但假设使用const 是最明显的原因。我会更新它以避免混淆。
【解决方案3】:

这是您造成此问题的误解:

[..] 不会被类的方法改变

成员变量被你的类的一个方法改变了,赋值运算符。包括编译器合成的那个。如果将成员变量标记为const,则表示该变量将(不应该!)在对象的生命周期内更改其值。很明显,为对象分配一个新值违反了这个陈述。因此,如果您确实不希望成员更改,请不要更改const

【讨论】:

    【解决方案4】:

    是的,您可以覆盖赋值运算符。

    因为您使用的是默认值,所以编译器也会尝试复制 const 成员。这是非法的,因为它是const

    class A
    {
    private:
       const int a;
    public :
       A() : a(0) {}
       A& operator = (const A& other) {return *this;}
    };
    
    int main()
    {
       A a;
       A b;
       a = b; //this is legal if operator = is declared
    }
    

    【讨论】:

    • 那么const成员的代价就是可以提供一个赋值运算符,但只能提供一个坏掉的?
    • @UncleBens 为什么这个坏了?不是,只是没有修改const 成员......这应该是有道理的,对吧?
    • @UncleBens:鉴于 lhs 上的 const 成员可能与 rhs 上的成员相同,因为它仅在构造函数中设置,因此复制它没有意义。如果它在不同的构造函数中初始化不同,那么您可能希望重新考虑您的设计!
    • const成员的“代价”是,如果要赋值,需要自己定义赋值运算符。我认为这是一个功能。
    • @Johnsyweb:如果所有实例的 const 成员都相同(例如在给定的示例中),为什么它是非静态成员? - 也许更现实的例子是A::A(int init_value): a(init_value)。现在(任何) operator= 重载有什么意义?
    猜你喜欢
    • 2014-05-18
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 2014-10-07
    • 2023-03-24
    • 2021-06-28
    相关资源
    最近更新 更多