C++ 通过公共常量指针进行封装是个好主意吗?

C++ 通过公共常量指针进行封装是个好主意吗?,c++,oop,C++,Oop,我知道的是: 明智的做法是不要在API中直接公开IVAR;而是使用访问器 指向非const对象的const指针意味着可以更改对象,但不能重定向指针指向的位置 以下是我的情况: 我有一些相关的课程。我想创建一个简单的类,通过组合将它们组合成一个逻辑接口。我的每个封闭类在其API中都有公共和私有的区别,所以我不介意将它们直接公开给父类的用户。这意味着为这些IVAR编写访问器对我来说太过分了,因为类已经管理了哪些是公共的,哪些不是。但是,我不希望用户更改包含在这个组合父类中的实际对象 所以我能想到

我知道的是:

  • 明智的做法是不要在API中直接公开IVAR;而是使用访问器
  • 指向非
    const
    对象的
    const
    指针意味着可以更改对象,但不能重定向指针指向的位置
以下是我的情况:

我有一些相关的课程。我想创建一个简单的类,通过组合将它们组合成一个逻辑接口。我的每个封闭类在其API中都有公共和私有的区别,所以我不介意将它们直接公开给父类的用户。这意味着为这些IVAR编写访问器对我来说太过分了,因为类已经管理了哪些是公共的,哪些不是。但是,我不希望用户更改包含在这个组合父类中的实际对象

所以我能想到的唯一方法就是使用
const
指针指向这些对象,例如:

class Parent{

  public:

   EnclosedClass1*const ec1; //users can enjoy the public API of EnclosedClass
   EnclosedClass2*const ec2;

   void convenienceMethod(){
         //performs inter-related functions on both ec1 and ec2
    }

 }
通过这种方式,让某人直接与
ec1
ec2
的API接口没有害处,但是,我想确保这是相当简单的,因为至少此人不能更改正在操作的实际资源,因此
const
指针

这有意义吗?它是常量指针的一个很好的用法吗


或者,我可以让它们完全私有,忘记指针(这个类无论如何都管理这些对象),只需做额外的工作,为这些对象中包含的公共函数编写访问器。但这似乎有点过分了,对吧?

传统的面向对象方法是保持数据的私有性,如果想在类外使用这些数据,可以使用公共访问器

如果不希望修改私有数据指针,那么不提供setter,只提供getter

class Parent{

  private:
       EnclosedClass1*const ec1;
       EnclosedClass2*const ec2;

  public:
      EnclosedClass1* getEnclosedClass1() const {...};
      EnclosedClass2* getEnclosedClass2() const {...};    

      void convenienceMethod(){
    }
}

另一种可能性(如下文评论中的@pmr建议)是:


使用const reference可以防止用户测试getter返回的值。但您必须确保对象状态是一致的,并且永远不会有指向
nullptr
的内部指针;在这种情况下,您应该抛出一个异常。

如果您有一个类包含两个其他对象,并向其客户端提供对这些成员的免费和完全访问,那么您应该考虑您的类是否具有正确的设计。demeter法则不会因为不使用getter而失效,即parent.ec1->foo并不比parent.getEc1().foo好多少


在任何情况下,const成员通常都会遇到更多的麻烦。

这有意义吗?对

这是常量指针的一个很好的用法吗?嗯,不是真的。规范的编码方式是为每个元素提供一个getter函数,并使内部数据私有化。采用另一种方法会让代码的读者想知道为什么要这样做,因为这不是标准做法


因此,将常量指针设为私有,并创建getter来访问它们。出于性能原因,您可以使这些getter
内联
d函数,这样不会对生成的可执行文件产生任何影响,但会大大提高代码的可读性。

这种方法有不同的错误。第一个是,从封闭类型的角度来看,成员的接口有公共和私有两个方面,这并不意味着什么。完整类型可以有一组不同的不变量,用户可以通过独立修改两个子对象来打破这些不变量

即使
父类
类型没有任何不变量(即,它不依赖于子对象的状态),建议的方法也是不正确的。给定一个
const父对象&
调用方可以对两个子对象应用任何操作(const和not const),从而有效地更改
父对象的状态。在这种情况下,更好的方法是直接存储子对象:

class Parent {
public:
   EnclosedClass1 ec1;
   EnclosedClass2 ec2;
...

但是我建议您提供访问器来提取数据,并提供变异器(如果这个词有意义的话)来更改对象的状态。这样,如果您以后需要添加不变量,您可以将它们固定在那里。

原因之一可能是它可以通过constcast转换为非常量指针。

决定类应该做什么,并编写一个提供这些功能的接口。如果允许用户处理内部数据很重要,那么就编写接口来处理。但是,在大多数情况下,类呈现的抽象不同于您可以对其内部数据执行的操作的总和,因此接口应该而不是公开内部数据。例如,保存学生姓名的类通常会将其实现为某种容器,其中包含
std::string
数据。该类不应为人名提供指向字符串的指针或引用。相反,如果一个名称需要更正,例如,这应该在一个单独的编辑器类中完成;更正后,存储的名称可以用新版本更新。类不需要向用户公开整个
std::string
接口。

好的,请理解,但是在这种情况下使用getter而不仅仅是使用const指针有什么好处?似乎允许类似的限制。这只是促进基于行为的接口编程的习惯。此外,这也是遵循坚实原则的一种方式:得墨忒尔定律。如果没有人分配给getter,为什么要在getter中返回指针?为什么不返回一个引用/对const的引用呢?getter不应该也是
const
?@tadman是的,这样会更干净,我要纠正这个问题。“额外的工作…似乎有些过头了,对吧?”不,这似乎是一个合适的解决方案。你不让其他对象这样做是有原因的,其中很大一部分是分离
class Parent {
public:
   EnclosedClass1 ec1;
   EnclosedClass2 ec2;
...