【问题标题】:Class variables: public access read-only, but private access read/write类变量:公共访问只读,但私有访问读/写
【发布时间】:2011-07-22 09:30:22
【问题描述】:

哎呀,暂时不在那个套接字库上工作。我正在尝试在 C++ 方面对自己进行更多的教育。

对于类,有没有办法使变量对公众只读,但在私有访问时读+写?例如像这样:

class myClass {
    private:
    int x; // this could be any type, hypothetically

    public:
    void f() {
        x = 10; // this is OK
    }
}

int main() {
    myClass temp;

    // I want this, but with private: it's not allowed
    cout << temp.x << endl;


    // this is what I want:

    // this to be allowed
    temp.f(); // this sets x...

    // this to be allowed
    int myint = temp.x;

    // this NOT to be allowed
    temp.x = myint;
}

简而言之,我的问题是如何允许从 f() 内完全访问 x,但从其他任何地方进行只读访问,即允许 int newint = temp.x;,但不允许 temp.x = 5;?类似于 const 变量,但可从 f() 写入...

编辑:我忘了提到我打算返回一个大向量实例,使用 getX() 函数只会复制它,它并不是真正的最佳选择。我可以返回一个指向它的指针,但这是 iirc 的不好做法。

P.S.:如果我只是想基本上展示我的指针知识并询问它是否完整,我会在哪里发布?谢谢!

【问题讨论】:

    标签: c++ access-modifiers


    【解决方案1】:

    当然可以:

    class MyClass
    {
        int x_;
    
    public:
        int x() const { return x_; }
    };
    

    如果您不想复制(对于整数,没有开销),请执行以下操作:

    class MyClass
    {
        std::vector<double> v_;
    
    public:
        decltype(v)& v() const { return v_; }
    };
    

    或使用 C++98:

    class MyClass
    {
        std::vector<double> v_;
    
    public:
        const std::vector<double>& v() const { return v_; }
    };
    

    这不会进行任何复制。它返回一个reference to const

    【讨论】:

    • 好的,我想这就是我想要的。我会使用:MyClass mclass; vector&lt;double&gt; *temp = mclass.x()?
    • @Alexandre,很好的例子。关于您的解决方案的最后一个问题:如果 mclass 被破坏,x tmp 的引用是否有未定义的内容/行为?
    • @FurryHead:好点:是的,在它被破坏的情况下,访问引用的对象是未定义的行为。这种情况称为“悬空引用”。在这方面,引用与指针非常相似。
    • 我仍然看不到只返回一个元素的std::vector 的优势。我的问题仍然适用于 POD 类型。 std::vector 需要将返回值复制到向量中;或者在某些时候必须将值复制到向量中:附加复制指令。单独返回项目是一种复制操作。我很困惑。
    • @ThomasMatthews Alexandre 只是给出了两个不同的例子。如果你有一个 int 不要返回一个 const ref,如果你有一个 std::vector 返回一个 const ref。我们并不是要将第一个示例中的单个 int 存储在向量中。
    【解决方案2】:

    虽然我认为返回 const T&amp; 的 getter 函数是更好的解决方案,但您几乎可以使用您要求的语法:

    class myClass {
        private:
        int x_; // Note: different name than public, read-only interface
    
        public:
        void f() {
            x_ = 10; // Note use of private var
        }
        const int& x;
        myClass() : x_(42), x(x_) {} // must have constructor to initialize reference
    };
    
    int main() {
        myClass temp;
    
        // temp.x is const, so ...
        cout << temp.x << endl; // works
        // temp.x = 57;  // fails
    
    }
    

    编辑:使用代理类,您可以准确地获得您要求的语法:

    class myClass {
    public:
    
        template <class T>
        class proxy {
            friend class myClass;
        private:
            T data;
            T operator=(const T& arg) { data = arg; return data; }
        public:
            operator const T&() const { return data; }
        };
    
        proxy<int> x;
        // proxy<std::vector<double> > y;
    
    
        public:
        void f() {
            x = 10; // Note use of private var
        }
    };
    

    temp.x 在类中似乎是可读写的int,但在main 中是只读的int

    【讨论】:

    • 为什么不呢。我对此表示赞成,因为这会努力获得所需的行为。但是,如果你有例如。 void f(MyClass)MyClass 可以由int 构造,你不能做f(myObj.x),所以行为不太一样。
    • @AlexandreC.:我认为使用 C++11,您可以将构造函数设置为显式,如下所示:explicit MyClass(int s);,这将防止隐式创建临时 MyClass。
    • @Markus:实际上我的观点恰恰相反:您不能链接用户定义的转换,最多允许一次。由于您已经花费了转换 proxy&lt;int&gt; -&gt; int,因此您不能使用 myObj.x 作为函数的参数,该函数期望可以从 int 隐式构造的东西。 (C++03 也允许explicit
    • @AlexandreC。它不一样,但我现在想不出一个用例,我需要你所说的那种功能。在什么情况下这会特别有用?我只能想到涉及生成代码的情况,您无法修改或不想修改生成器。
    • 谢谢你,这就是我想要的:简单明了的解决方案。
    【解决方案3】:

    一个简单的解决方案,like Rob's,但没有构造函数:

    class myClass {
    private:
        int m_x = 10; // Note: name modified from read-only reference in public interface
    public:
        const int& x = m_x;
    };
    
    int main() {
        myClass temp;
    
        cout << temp.x << endl; //works.
        //temp.x = 57;  //fails.
    }
    

    它更像get 方法,但更短。

    常量指针很简单,应该适用于您可以指向的所有类型。

    【讨论】:

    • 我相信这应该是这个问题的最佳答案。
    【解决方案4】:

    这可能会做你想做的事。

    如果您想要一个只读变量,但不希望客户端更改他们访问它的方式,请尝试使用这个模板类:

    template<typename MemberOfWhichClass, typename primative>                                       
    class ReadOnly {
        friend MemberOfWhichClass;
    public:
        inline operator primative() const                 { return x; }
    
        template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
        template<typename number> inline number operator+ (const number& y) const { return x + y; } 
        template<typename number> inline number operator- (const number& y) const { return x - y; } 
        template<typename number> inline number operator* (const number& y) const { return x * y; }  
        template<typename number> inline number operator/ (const number& y) const { return x / y; } 
        template<typename number> inline number operator<<(const number& y) const { return x <<y; }
        template<typename number> inline number operator>>(const number& y) const { return x >> y; }
        template<typename number> inline number operator^ (const number& y) const { return x ^ y; }
        template<typename number> inline number operator| (const number& y) const { return x | y; }
        template<typename number> inline number operator& (const number& y) const { return x & y; }
        template<typename number> inline number operator&&(const number& y) const { return x &&y; }
        template<typename number> inline number operator||(const number& y) const { return x ||y; }
        template<typename number> inline number operator~() const                 { return ~x; }
    
    protected:
        template<typename number> inline number operator= (const number& y) { return x = y; }       
        template<typename number> inline number operator+=(const number& y) { return x += y; }      
        template<typename number> inline number operator-=(const number& y) { return x -= y; }      
        template<typename number> inline number operator*=(const number& y) { return x *= y; }      
        template<typename number> inline number operator/=(const number& y) { return x /= y; }      
        template<typename number> inline number operator&=(const number& y) { return x &= y; }
        template<typename number> inline number operator|=(const number& y) { return x |= y; }
        primative x;                                                                                
    };      
    

    使用示例:

    class Foo {
    public:
        ReadOnly<Foo, int> x;
    };
    

    现在您可以访问 Foo.x,但不能更改 Foo.x! 请记住,您还需要添加按位和一元运算符!这只是一个让您入门的示例

    【讨论】:

    • 关于如何将其扩展到任意类的任何想法,而不仅仅是原语?最好避免容器类必须使用特殊语法进行读写访问。
    • 另外,不幸的是,这不能很好地与派生类一起使用。假设您有 class Bar : public Foo,您似乎无法让 Foo 的成员公开只读,但 读写Bar 中的成员函数可访问(非继承和友谊的非传递性)。
    • 这是为原语设计的。作为对象的类成员可以具有 const 函数以使它们“只读”。为了使包含类可以访问它们(与问题有关),使该类成为朋友类并使非常量函数私有。
    • 但是我需要让派生类成为ReadOnly 的朋友,但是如果继承类有ReadOnly 成员并且还不知道它的派生类,就没有办法知道让ReadOnly 的派生类成为朋友。因此,继承类的 ReadOnly 成员不能被派生类“写入”。查看新问题stackoverflow.com/q/26383155/148668
    • 这可以通过使用 ganeric 接口来解决,只要派生使用的接口比你好
    【解决方案5】:

    您可能想模仿 C# properties 进行访问(取决于您的目标、预期环境等)。

    class Foo
    {
      private:
        int bar;
    
      public:
        __declspec( property( get = Getter ) ) int Bar;
    
        void Getter() const
        {
          return bar;
        }
    }
    

    【讨论】:

      【解决方案6】:

      有一种使用成员变量的方法,但这可能不是可取的方法。

      有一个可写的私有成员和一个 const 引用公共成员变量,该变量为其自己的类的成员起别名。

      class Foo
      {
        private:
            Bar private_bar;
      
        public:
            const Bar& readonly_bar; // must appear after private_bar
                                    // in the class definition
      
        Foo() :
             readonly_bar( private_bar )
        {
        }
      };
      

      这会给你你想要的。

      void Foo::someNonConstmethod()
      {
          private_bar.modifyTo( value );
      }
      
      void freeMethod()
      {
          readonly_bar.getSomeAttribute();
      }
      

      你能做什么和你应该做什么是不同的事情。我不确定我刚刚概述的方法是否流行并且会通过许多代码审查。它还不必要地增加 sizeof(Foo) (尽管增加了少量),而简单的访问器“getter”不会,并且可以内联,因此它也不会生成更多代码。

      【讨论】:

      • 您误解了参考。在这种情况下,引用不会占用 c++ 所需的任何额外内存。见stackoverflow.com/questions/1179937/…
      • 如果你这样做const Bar&amp; readonly_bar = private_bar,那就更好了,因为编译器不会错过优化。
      【解决方案7】:

      您必须将其保留为私有,然后创建一个函数来访问该值;

      private:
      
          int x;
      
      public:
      
          int X()
          {
              return x;
          }
      

      【讨论】:

        【解决方案8】:

        正如其他答案中提到的,您可以通过将其设为私有并定义 getter 函数但不定义 setter 来为类成员创建只读功能。但这对每个班级成员来说都是很多工作。

        您还可以使用宏自动生成 getter 函数:

        #define get_trick(...) get_
        #define readonly(type, name) \
        private: type name; \
        public: type get_trick()name() {\
            return name;\
        }
        

        那么你就可以这样来上课了:

        class myClass {
            readonly(int, x)
        }
        

        扩展为

        class myClass {
            private: int x;
            public: int get_x() {
                return x;
            }
        }
        

        【讨论】:

          【解决方案9】:

          您需要将成员设为private 并提供public getter 方法。

          【讨论】:

          • 正如我刚刚对 appleskin 的回答发表评论并编辑了我的帖子,这并不是最优的,因为这会使变量的副本返回,并且我计划返回一个大向量。跨度>
          • @FurryHead:那么你当然应该通过 const 引用返回。无论如何,在我回答时,该信息不可用。
          【解决方案10】:

          我知道在 c++ 类中授予对私有数据成员只读访问权限的唯一方法是拥有一个公共函数。在你的情况下,它会喜欢:

          int getx() const { return x; }

          int x() const { return x; }.

          默认情况下,通过将数据成员设为私有,您可以使其在类之外的范围内不可见(也就是无权访问)。本质上,类的成员对私有数据成员具有读/写访问权限(假设您没有将其指定为const)。类的friends 可以访问私有数据成员。

          请参阅here 和/或任何good C++ book 访问说明符。

          【讨论】:

          • 感谢您的参考。我只是在使用 private: 做例子,我不想要 private,因为它会阻止所有访问。我希望它在公共场合表现得像一个 const,但在本地表现得像一个非常量。
          【解决方案11】:

          编写一个公共 getter 函数。

          int getX(){ return x; }
          

          【讨论】:

          • 问题在于,我应该提到的是,我打算返回的是一个大向量 - 这并不是最佳的,因为它正在制作副本。
          • 问题在于 x 不是 void :) @FurryHead: 然后返回一个 const 引用。
          • 喜欢const int* getX() { return &amp;x; } ?
          • @FurryHead:差不多(你的方法还可以,但有更好的解决方案)。您可能想阅读 C++ 书籍中的参考资料。它们提供您正在寻找的语义。
          • Okie dokie。有什么书可以推荐吗? :-D
          【解决方案12】:

          但是 temp.x = 5;不允许?

          这在发布的 sn-p 中是不允许的,因为它无论如何都被声明为 private 并且只能在类范围内访问。

          这里要求访问

          cout

          但这里不是为了-

          int myint = temp.x;

          这听起来很矛盾。

          【讨论】:

          • 我知道。但是一个 private: 变量不能用 int myint = temp.x; 访问 - 我想要读取访问权限,而不是对公共的写入访问权限。
          • 如果是private变量,只能在类的范围内访问。(句点)
          • 我只是在使用 private: 做例子,我不想要 private,因为它会阻止所有访问。我希望它在公共场合表现得像一个 const,但在本地表现得像一个非常量。
          猜你喜欢
          • 2012-12-28
          • 1970-01-01
          • 2022-09-24
          • 2019-07-15
          • 1970-01-01
          • 2012-05-29
          • 2011-02-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多