【问题标题】:Is it possible to declare a class member both const/non-const?是否可以同时声明 const/non-const 类成员?
【发布时间】:2019-04-01 22:42:55
【问题描述】:

我有一个类,它可以将非 const 指针或 const 指针作为其重载构造函数的参数。在我的特殊情况下,我需要从类constconstnon-const 方法实例化此类的一个对象@ ,但它从const 方法失败,因为它无法将const 指针分配给foo .

myClass() {
public:
    myClass(T* v);
    myClass(const T* v);
    // ...
    T* foo;
    // ...
}

是否可以将两个构造函数中的参数分配给foo?如果是这样,正确的语法是什么?

编辑:

在更具体的情况下,我有一个类 myClass 环绕 std::vector 并允许我通过嵌套类 mySubset 直接访问向量的子集:

 template<typename _type>
myClass() {
    std::vector<_type> data;
public:
    class mySubset(){
        myClass<type>* foo;
    public:
        mySubset(myClass<_type>* _in) { foo = _in; };
        mySubset(const myClass<_type>* _in) { foo = _in; /* error */ };
        // ...
    }
    // ...
    myClass();
    // ...
    void mySubset method() { return mySubset(this); };;
    void mySubset const_method const() { return mySubset(this); /* error */ };
    // ...
}

其中的代码无关紧要 - 基本上mySubset 允许读取和写入特定的向量位置。虽然我能够使用单独的 const 和非 const 嵌套类来实现我想要的,但我一直在寻找一种使用单一返回类型的方法。

【问题讨论】:

  • 你能写一个五六行的程序来演示这个问题吗?
  • 取决于上下文。请添加上下文以获得答案。你可以const_cast 离开const,但是你冒着修改不应修改的T 的风险。无论是谁做到了const,都可能有充分的理由这样做。如果你持有T 并且从不修改它const T* foo; 可能是你最好的朋友。
  • 将班级成员改为const T* foo
  • 简单的答案是:不能有两个同名的成员。你需要使用不同的名字(见下面的例子)当你两者都需要,const 和 non-const 版本。在一些可能的情况下,您需要这样的构造,但我认为,还有其他可能的方法来解决这个问题。这一切都取决于上下文/请求的功能。它可以只使用 const 成员,或者使用另一个可以使用非 const 版本的 (child class) 来完成。要获得有意义的答案,请添加有关您的问题和背景的更多详细信息。
  • 我用一个更详细的例子编辑了我的答案

标签: c++ class constants


【解决方案1】:

如果您不想使用参数化类型(模板)类作为@P0W 的答案,那么您不可能只使用一个指针来接受所有常量和非常量指针类型。您需要另一种常量指针类型才能在包装类中仅接受 const &lt;your another class&gt; *。 在您可能不喜欢的包装类中有两个单独的指针类型后,下面的代码就可以工作了。

#include <iostream>

using namespace std;

class SomeObject {
public:
    SomeObject(){}
    explicit SomeObject(int i):testVal(i){}
private:
    int testVal;
};

class PtWrapper {
public:
    PtWrapper(SomeObject *pso);
    PtWrapper(const SomeObject *cpso);
private:
    SomeObject *pSO;
    const SomeObject *cpSO;
};



int main(int argc, char *argv[]) {
    SomeObject so(133);
    SomeObject *pso = &so;
    const SomeObject cso(166);
    const SomeObject *cpso = &cso;
    PtWrapper pw1(pso);
    PtWrapper pw2(cpso);
    return 0;
}

PtWrapper::PtWrapper(SomeObject *pso) :pSO(pso){

}

PtWrapper::PtWrapper(const SomeObject *cpso):cpSO(cpso){}

【讨论】:

    【解决方案2】:

    您可以在 const T* 构造函数中使用 const_cast,但通常不应该这样做。

    const T* 表示“指向一个常数值 T”,而您存储了一个“指向 T 的指针”。如果您进行 const 强制转换,您最终可能会修改不应修改的值。如果您不打算修改 foo,只需将其声明为 const T* 并使用单个 const T* 构造函数即可。

    我会检查一下这是否是设计问题。很多时候这些场景会出现: (1) 当它应该是 const 时,你将一个指向非 const 的指针存储在哪里。通常这是因为您正在访问另一个对象中的值,并且您应该在每个使用站点将对象作为(可能是 const)引用传递,而不是存储指向它的指针。 (2) 当你真的想存储一个对象的副本时,在这种情况下,你只需保留一个常规的T 并将其作为const T&amp; 传入构造函数中。 (3) 您正在处理原始 C 风格的字符串,并希望将内容复制到您自己的缓冲区中。

    【讨论】:

      【解决方案3】:

      我认为你必须重新考虑你的设计,因为你不能用const T* 左值初始化T*,没有const_cast,除非你真的很确定,否则应该避免,(因为它调用如果您尝试在丢弃其 const 之后修改 const 指针,则会出现未定义的行为)

      相反,您可以对constnon const 使用模板类型推导

      template <typename T>
      class myClass {
      public:
          //myClass(T* v):foo(v) { }
          myClass( T* v):foo(v)
          {
          }
          // ...
          T* foo;
          // ...
      };
      

      那么,

      int a =42;
      const int* p1 = &a;
      int *p2 = &a;
      
      myClass  X1(p1); //C++17 auto type deduction or use myClass<const int> X1(p1)
      myClass  X2(p2);
      

      【讨论】:

      • 这是实现这一目标的一种方法,但使用模板似乎有点矫枉过正,因为我只需要为 2 种类型定义它
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多