首页 > 解决方案 > 当要访问的对象被多次封装时,如何正确使用setter?

问题描述

我经常为这个问题而苦苦挣扎,找不到任何明确的解决方案。我想我知道 getter/setter 的动机。

先验信息:

在实现现实生活中的数据时,通常将数据封装在不止一层中。例如:

// 1st stage data types ------------------------------
struct Cartesian
{
    int32_t x;
    int32_t y;
    int32_t z;
}

struct GeoLocation
{
    double_t latitude;
    double_t longitude;
    int32_t altitude;
}

// 2nd stage data types ------------------------------
struct Drone
{
    Cartesian baseOffset; // m
    Cartesian velocity; // m/s
}

struct Plane
{
    GeoLocation location; // semicircle
    Cartesian velocity; // knots
}

// 3rd stage data types ------------------------------
struct Swarm
{
    Plane base;
    Drone member[10];
}

在 C++ 中,我使用类而不是结构(因为为什么不呢?)。Swarm[3].member[8].velocity.x当通过某个通信通道接收到数据时,就会出现问题。意识到一个系统中可以有不止一个群体。

要求:

根据 MISRA C++ 规则,函数不能返回对任何类成员的非常量引用,因为在没有该类的许可/知识的情况下不应更改该成员。

问题:

当我使用 getter 和 setter 时,我不能说“ Swarm[3].member[8].velocity.x”;相反,我可以用几种方式这么说:

1.这是不允许的,因为 get() 函数返回 const 引用并且不能调用 set()。

Swarm[3].getMember(8).getVelocity().setX(5); (或set("X", 5)

2.这个方法把所有的负担都带入了 Swarm 类。虽然代码对于调用 Swarm 类的人来说似乎更短,但在发生变化时编写代码和在后台进行维护是非常繁重的。

Swarm[3].setMemberVelocity(8,X,5)

3.这种方法有点介于两者之间,但这里的问题是您可能会牺牲效率,因为每次新数据首先到达时,您都会创建一个临时变量,获取它,填充它并设置它。

Cartesian tempVelocity = Swarm[3].getMember(8).getVelocity();

tempVelocity.x = 5;

Swarm[3].setMemberVelocity(8, tempVelocity);

这三种方法中哪一种最好?或者有什么我可以使用的替代品吗?

提前致谢。

标签: c++embeddedsettergettermisra

解决方案


我可能不完全了解 MISRA。然而,它似乎对灵活的设计适得其反(而且根本不是 c++ish)。假设我发现我需要:

struct SuperSwarm {
  Swarm swarms[10];
};

然后按照您的选项 2,我需要为所有内部聚合实现一系列 setter/getter,因为在您的情况下听起来有点像您需要能够单独设置所有数据。如果您需要更改某些内容,则会发生同样的事情,Drone那么所有内容都需要更新。对于一个好的和灵活的设计,你可以看到这真的是一场噩梦。遵守您制定的规则并没有真正的好选择。这就是为什么您通常返回一个非常量引用并在不需要特殊处理时使用它的原因。在某些方面,这可以使您的代码面向未来,而不仅仅是一个公共成员变量。

您可以“作弊”(并使用您的选项 3)并且在设计方面仍然保持灵活的一种方法是代理所有内容 - 例如std::shared_ptr. 当您返回 astd::shared_ptr时,它似乎是一个副本并且它确实是 - 但它实际上只是同一个指向对象的代理(这类似于其他一些 OO 编程语言中的事情如何在幕后工作)。不过,您可能需要更合适的代理类。但话又说回来,又何必呢?为什么不只使用结构并说它是一种数据结构,而不是某个承担(另一个)责任的类。毕竟,你会清楚地表达你的意图。


推荐阅读