C++:访问对象内部子对象的方法

C++ : Access a sub-object's methods inside an object

本文关键字:对象 方法 内部 访问 C++      更新时间:2023-10-16

我开始编写更大的对象,里面有其他对象。 有时,我需要能够从包含子对象的类之外调用子对象的方法,例如从main()函数调用。

到目前为止,我正在学习中使用getter和setter。 这将给出类似于以下代码的内容:

class Object {
public:
bool Object::SetSubMode(int mode);
int Object::GetSubMode();
private:
SubObject subObject;
};
class SubObject {
public:
bool SubObject::SetMode(int mode);
int SubObject::GetMode();
private:
int m_mode(0);
};
bool Object::SetSubMode(int mode) { return subObject.SetMode(mode); }
int Object::GetSubMode() { return subObject.GetMode(); }
bool SubObject::SetMode(int mode) { m_mode = mode; return true; }
int SubObject::GetMode() { return m_mode; }

这感觉非常次优,迫使我为需要从外部访问的每个方法编写(丑陋的)代码。我希望能够做一些像Object->SubObject->Method(param);这样简单的事情

我想到了一个简单的解决方案:将子对象作为公共对象放在我的对象中。 这样,我应该能够简单地从外部访问其方法。 问题是,当我学习面向对象编程时,我被告知除了方法之外,把任何东西放在公共场合都是亵渎神明,我不想开始养成不良的编码习惯。 在这里发布之前,我在研究中遇到的另一个解决方案是添加一个指向子对象的公共指针?

如何以简洁的方式访问子对象的方法?

是否允许/将对象作为公共对象放入类中以访问其方法是一种好的做法?否则怎么办?

非常感谢您对此的帮助。

指针和公共成员对象的问题在于您刚刚删除了隐藏的信息。 您的代码现在更加脆弱,因为它"知道"您已经实现了具有 4 个对象 Wheel 成员的对象 Car。 而不是像这样调用隐藏细节的 Car 函数:

Car->SetRPM(200);  // hiding

你想像这样直接开始旋转轮子:

Car.wheel_1.SetRPM(200);  // not hiding! and brittle!
Car.wheel_2.SetRPM(200);

如果你改变类的内部结构呢? 以上内容现在可能已损坏,需要更改为:

Car.wheel[0].SetRPM(200);  // not hiding!
Car.wheel[1].SetRPM(200);

此外,对于您的汽车,您可以说 SetRPM(),该类会确定它是前轮驱动、后轮驱动还是全轮驱动。 如果您直接与滚轮成员交谈,则不再隐藏实现细节。

有时,您确实需要直接访问类的成员,但创建类的一个目标是封装和隐藏调用方的实现详细信息。

请注意,您可以使用 Set 和 Get 操作来更新类中的多位成员数据,但理想情况下,这些操作对 Car 本身有意义,而不是特定成员对象。

有人

告诉我,除了方法之外,把任何东西放在公共场合都是亵渎神明

像这样的笼统声明是危险的;您必须考虑每种风格的优点和缺点,但彻底禁止公共成员是一个坏主意 IMO。

拥有公共成员的主要问题是它公开了可能更好地隐藏的实现细节。 例如,假设您正在编写一些库:

struct A {
struct B {
void foo() {...}
};
B b;
};
A a;
a.b.foo();

几年后,您决定要根据上下文更改A的行为;也许您想让它在测试环境中以不同的方式运行,也许您想从不同的数据源加载,等等。 哎呀,也许你只是决定成员的名字b没有足够的描述性。 但由于b是公开的,因此无法在不破坏客户端代码的情况下更改A的行为。

struct A {
struct B {
void foo() {...}
};
struct C {
void foo() {...}
};
B b;
C c;
};
A a;
a.c.foo(); // Uh oh, everywhere that uses b needs to change!

现在,如果你要让A包装实现:

class A {
public:
foo() { 
if (TESTING) {
b.foo();
} else {
c.foo();
}
private:
struct B {
void foo() {...}
};
struct C {
void foo() {...}
};
B b;
C c;
};
A a;
a.foo(); // I don't care how foo is implemented, it just works

(这不是一个完美的例子,但你明白了。

当然,这里的缺点是它需要很多额外的样板,就像你已经注意到的那样。 所以基本上,问题是"你是否期望实现细节在未来发生变化,如果是这样,现在添加样板文件或以后重构每个调用的成本会更高吗? 如果你正在编写外部用户使用的库,那么"重构每个调用"就会变成"破坏所有客户端代码并强制它们重构",这会让很多人非常沮丧。

当然,与其在SubObject中为每个函数编写转发函数,不如为subObject添加一个getter:

const SubObject& getSubObject() { return subObject; }
// ...
object.getSubObject().setMode(0);

它遇到了一些与上面相同的问题,尽管它更容易解决,因为SubObject接口不一定与实现相关联。

综上所述,我认为有时公众成员肯定是正确的选择。 例如,简单结构,其主要目的是充当另一个函数的输入,或者只是从 A 点到 B 点获取一堆数据。 有时,所有这些样板文件真的是矫枉过正。