C++ API:修改内部对象

C++ API: Modifing internal objects

本文关键字:内部对象 修改 API C++      更新时间:2023-10-16

我有两个相关的问题。 目前,我正在设计/编写一个C++ API,其中我需要能够修改由另一个对象持有的对象。

它与此示例相当:

class Bar
{
public:
Bar(int x) : num(x){}
void setNum(int x)
{
num = x;
}
int getNum()
{
return num;
}
private:
int num;
};
class Foo
{
public:
Foo() = default;
void setBar(std::unique_ptr<Bar> newBar)
{
bar = std::move(newBar);
}
Bar* getBar()
{
return bar.get();
}
private:
std::unique_ptr<Bar> bar;
};

然而,类Foo拥有Bar的所有权,Bar必须能够被修改。 在这里,Foo是用户将与之交互的主要类。 虽然Bar可以更多地被视为一种数据类型,但它会改变Foo的输出。

返回原始指针以Bar的解决方案是首选选项吗? 我有一种感觉,这阻止了封装,这对于 API 设计来说是不行的。 我在谷歌上搜索的努力还没有给我一个具体的答案。 但我可能只是用错误的搜索词看。

这个问题的第二部分是,如果Bar存储在Foo的容器中,此示例将如何变化。 我会返回指向整个容器的指针,容器的迭代器吗?

如果您担心getBar破坏封装,那么您还应该void setBar(std::unique_ptr<Bar> newBar)视为这样的破坏。

因为允许从外部设置bar确实使得关于bar的知识可能没有专属于Foo,而传递给FooBar的知识可能仍然有可能修改bar,因此Foo不能对Bar的状态做任何假设,因为它可以随时更改。

另一方面,如果您只想对BarFoo进行读取访问,那么const Bar* getBar()const Bar& getBar()不会破坏封装,因为getBar不允许更改Bar

返回原始指针到 Bar 的解决方案是首选选项吗?

这是一个解决方案,不一定是一个坏解决方案。在对象始终存在的情况下,返回引用会更可取(在这种情况下不是,因为Foo的默认构造函数不会创建Bar(。

一些程序员更喜欢对裸指针使用包装器(例如observer_ptr,这已被提议用于标准(,以将其与旨在迭代数组的指针或拥有裸指针(应避免使用后者(区分开来。

我有一种感觉,这阻止了封装

你的整个前提是打破封装,因为你想"修改内部对象"。如果要避免封装中断,则可能需要进一步更改设计,以便无需修改内部对象(外部(。

不破坏封装的解决方案是提供一个特定的接口来Foo进行修改,例如:

void Foo::transmogrify_bar(int gadgets) {
bar->transmofgrify(gadgets);
}

此封装是否对您的 API 有用是另一回事。在某些情况下,这是必不可少的,而在其他情况下,这并不重要。

如果 Bar 将存储在 Foo 的容器中。我会返回指向整个容器的指针,容器的迭代器吗?

是否希望客户端能够修改容器(添加、删除元素(?

这进一步打破了封装。相反,您可以beginend不允许修改容器本身的迭代器,这会在单个对象的情况下对指针返回进行等效封装。

您可以仅提供 const 迭代器,并将迭代器作为参数添加到transmogrify以保持修改Bar的封装。

最后,对于完全封装,您需要使用 PIMPL 模式来完全隐藏Bar