【问题标题】:Swap method with const members使用 const 成员交换方法
【发布时间】:2010-02-05 21:01:14
【问题描述】:

我想为我的类(我们称之为 A)实现一个 Swap() 方法来生成复制和交换运算符 =()。据我所知,swap方法应该是通过交换类的所有成员来实现的,例如:

class A 
{
  public:
    void swap(A& rhv) 
    {
        std::swap(x, rhv.x);
        std::swap(y, rhv.y);
        std::swap(z, rhv.z);
    }
  private:
    int x,y,z;
};

但是,如果我有一个 const 成员,我该怎么办?我不能为它调用 std::swap,所以我不能编码 A::Swap()。

编辑:实际上我的课有点复杂。我想序列化和反序列化它。 const 成员是该对象内不会更改的一段数据(例如其 ID)。所以我想写这样的东西:

class A
{
  public:
    void Serialize(FILE* file) const
    {
        fwrite(&read_a, 1, sizeof(read_a), file);
    }

    void Deserialize(FILE* file) const
    {
        size_t read_a;
        fread(&read_a, 1, sizeof(read_a), file);
        A tmp(read_a);
        this->Swap(tmp);
    }

 private:
   const size_t a;
};

并调用此代码:

A a;
FILE* f = fopen(...);
a.Deserialize(f);

我很抱歉措辞如此含糊。

【问题讨论】:

  • 请帮帮我。如果类中有 const 成员,operator=() 会做什么?我的意思是什么目的?
  • 如果我们了解更大的背景可能会有所帮助。你真正想要达到的目标是什么? class A 真的是什么?
  • @John (+1) @Neil 如果内存地址 1 表示的人 A (acct-id const) 的对象将被内存表示的人 B (acct-id const) 交换地址 2. 然后我认为交换对象没有什么害处——包括 acct-id ......交换是为了......(等待它......)交换!这并不意味着更改值... C++ 的 const 正确性适用于一组相当有限的用例。
  • @f0b0s:好的,很公平。但你不会把那个人换成另一个人。关键是当你有一个带有 const 成员变量的类和一个 swap 方法时,有些事情是不对的。成员不应该是 const 或者你不应该交换。一般来说。
  • 您的代码似乎仍然缺少一些东西。而且我仍然看不出你为什么要构建一个临时的并交换它。 (至少,你确定你想要一个公共交换方法。也许你只是想要一个私人助手,它只会交换你想要交换的东西?)

标签: c++ constants swap


【解决方案1】:

我认为您真正想要的是拥有一个可以在对象之间轻松交换的内部数据结构。例如:

class A 
{
   private:

     struct A_Data {
       int x;
       int y;
       const int z;

       A_Data(int initial_z) : z(initial_z) {}
    };

    std::auto_ptr<A_Data> p_data;

  public:

     A(int initial_z) : p_data(new A_Data(initial_z)) {}

     void swap(A& rhv) {
        std::swap(p_data, rhv.p_data);
     }
};

这使z 值在A 对象内部数据的任何实例中保持不变,但您可以交换两个A 对象的内部数据(包括常量z 值)而不会违反常量正确性。

【讨论】:

  • 是的,除了现在 A 的复制构造函数行为充其量是有问题的,它的赋值运算符完全被破坏了。所以如果你走这条路,别忘了写一个正确的复制构造函数和赋值运算符。
  • 嗯,是的,您仍然必须遵循“如果您需要以下任何一个:析构函数、复制构造函数、赋值运算符,那么您需要全部三个”的一般规则。 (并且交换是一种赋值运算符)。上面的代码旨在展示 const 正确交换背后的想法,而不是完整的实现。
  • 这使p_data-&gt;z 不可修改,但您可以更改整个A_Data 对象。通常如果你想要一个 const 成员是为了确保没有人改变它......
【解决方案2】:

睡个好觉后,我认为最好的答案是使用指向 const 值的非 const 指针——毕竟这些都是您要捕获的语义。

【讨论】:

  • 谢谢,你是唯一一个明白问题的人:我不想改变 VALUE,而是改变 const 变量的 ADDRESS(位置)。我已经使用 poiter 完成了这个,这是我认为最优雅的。
【解决方案3】:

f0b0s,一个好的设计原则是将你的对象设计成不可变。这意味着对象一旦创建就不能更改。要“更改”对象,您必须复制对象并确保更改所需的元素。

话虽如此,在这种情况下,您应该考虑使用复制构造函数来复制要交换的对象,然后实际交换对对象的引用。我可以理解仅仅能够在引擎盖下更改对象的元素是很诱人的,但是最好制作对象的副本并将对该对象的引用替换为 NEW 对象。这可以让你摆脱任何 const 的讨厌。

希望这会有所帮助。

【讨论】:

    【解决方案4】:

    我建议您使用指向实例的指针。与classstruct 中的数据相比,指针可以更容易地交换。

    交换一个常量值的唯一方法是创建另一个对象,或者clone当前对象。

    给定一个结构:

    struct My_Struct
    {
      const unsigned int ID;
      std::string        name;
      My_Struct(unsigned int new_id)
        : ID(new_id)
      { ; }
    };
    

    我的理解是你想交换上面My_Struct 之类的实例。您可以复制可变(非常量)成员,但不能复制 const 成员。更改const 成员的唯一方法是为const 成员创建一个具有新值的新实例。

    也许您需要重新考虑您的设计。

    【讨论】:

      【解决方案5】:

      恕我直言,您必须考虑不要交换 CONST 成员。

      PD:我认为您可以考虑在您的方法中使用反射。所以你不必维护这个功能。

      【讨论】:

      • C++ 不支持反射。
      • 谢谢泰勒,我还没有意识到这一点。 :)
      【解决方案6】:

      这就是创建const_cast 的原因。请记住不要把你的脚踢飞。

      编辑: 好的,我承认 - const_cast 根本不是针对这个问题而设计的。这可能适用于你的编译器,但你不能指望它,如果恶魔从你的鼻孔里飞出来,请不要怪我。

      【讨论】:

      • 这绝对不是创建const_cast 的原因。如果该成员确实是const,那么使用const_cast 启用对该成员的写入将导致未定义的行为。 const_cast 只允许写入不是const 的东西,但写入代码恰好只有一个指向constconst 引用的指针。
      • 我认为这不是一个好主意。据我所知,真正的 const 对象上的 const_cast 会导致未定义的行为。
      • (+1) 当语言的语义与手头问题的语义不匹配时使用 const_cast....
      • 嗯,有点。换句话说,当有人搞砸了并且你必须绕过它时使用 const_cast。
      【解决方案7】:

      tl;博士; : 这是未定义的行为。

      Reference/reason: CppCon 2017: Scott Schurr “Type Punning in C++17: Avoiding Pun-defined Behavior, @24m52s +- ”

      我的解释,例如:

      假设您创建了一个T 类型的对象,它有一些const 成员。您可以将此对象作为非常量引用传递给操作它的函数f(&amp;T),但您希望const 成员在调用后保持不变。 swap可以在非常量引用中调用,也可以发生在函数f内部,打破了const成员对调用者的前提。

      使用swap 的代码的每个部分都必须断言被交换的T 类型的对象不属于const 成员被假定为常量的任何上下文。这是无法自动验证的*。

      *我只是假设这是无法验证的,因为它似乎是停机问题不可判定性的延伸。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-24
        • 1970-01-01
        • 1970-01-01
        • 2011-05-08
        • 2012-07-21
        • 1970-01-01
        相关资源
        最近更新 更多