相互引用的 Const 对象

Const objects that refer to one another

本文关键字:Const 对象 引用      更新时间:2023-10-16

作为帮助可视化问题的动机(否则只需跳到下面的代码),假设我正在编写一个纸牌游戏,其中某些牌"在玩"可以让你"购买"手中的其他牌。例如,假设你有一手牌,比如说四张牌,里面有一张王牌,你决定玩它。然后你得到另一手四张牌,现在王牌允许你免费玩一张女王牌。等等。

现在,我想编写这个游戏。我创建了一个Card结构:

struct Card {
std::string name_;
Card(const std::string name) : name_{name} {}
std::string name() const { return name; }
};
const Card ace("ace");
const Card one("one");
// etc.

但后来我决定我不仅要记录一张卡的名称,还要记录它免费提供的卡以及免费提供这张卡的卡。像这样说:

struct Card {
std::string name_;
const Card* parent_; // card that gave this card for free
const Card* child_;  // card that this card gives for free
Card(const std::string name) : name_{name} {}
std::string name() const { return name; }
std::string parent() const { return parent_->name(); }
std::string child() const { return child_->name(); }
};

(这段代码显然是零碎的,不是很健壮,但我只是想了解这个原则。为了添加父母/孩子,我还创建了以下方法:

struct Card {
// ...
private:
void set_parent(const Card* c) {
parent_ = c;
}
public:
void add_child(Card& c) {
child_ = &c;
c.set_parent(this);
}
// ...
};

现在,我可以像这样创建Card对象:

Card ace("ace");
Card queen("queen");
// etc.
ace.add_child(queen);
// etc.

但是,我不得不牺牲Card对象的恒定性。让我明确指出,这只是一个基本的例子。我打算编写的代码在其Card类中有许多数据成员。因此,我想指出父/子卡而不明确复制数据。我也有很多"卡片",所以我宁愿不要复制尽可能多的数据。

我的问题是:我有什么办法将Card对象保留为 const (因为它们永远不会包含变量数据)?还是我想错了?

可能与这个问题相关的是隐含的:这甚至重要吗?我是否有充分的实际理由希望这个功能常量对象在代码中作为常量?即使没有,那么即便如此,有没有办法让这些对象只是出于兴趣。

正如你所指出的,卡的数据(它的身份加上一些关于它的规则)是恒定的。 此外,它们在编译时是已知的。 所以:

#include <array>
enum class Rank {
None, Ace, Two, Three, Four // ...
};
struct Card
{
Rank rank = Rank::None;
std::array<Rank, 2> from = {};
std::array<Rank, 2> to = {};
};
constexpr Card cards[] = {
{},
{Rank::Ace, {Rank::Two, Rank::Three}, {}},
{Rank::Two, {}, {Rank::Ace}},
{Rank::Three, {Rank::Four}, {Rank::Ace, Rank::Four}},
{Rank::Four, {Rank::Three}},
};

您可以根据需要使数组尽可能大。 现在,您拥有了一整套在编译时已知的数据,并且存储紧凑。

如果不破坏正确构造它们的规则,就无法构造相互引用的const对象。 但。。。。

您可以静态构造它们的读写,如privatestatic类成员,然后发布对它们的const引用。 这将使对这些成员的访问权限成为只读的。

样本.h

class Sample {
const int priv_ro;  /* this is true const field */
int       priv_rw;  /* this is read-write */
public:
const int& pub_ro_ro; /* read only alias */
const int& pub_rw_ro; /* idem */
//int& pub_ro_rw; /* error, cannot alias a const as read write */
int& pub_rw_rw; /* correct, public read write alias to private field */
static Sample global_rw; /* static global variable (read-write) */
static const Sample global_ro; /* static global variable (read-only) */
private:
Sample(int ro, int rw):
priv_ro(ro),
priv_rw(rw),
pub_ro_ro(this->priv_ro),
pub_rw_ro(this->priv_rw),
pub_rw_rw(this->priv_rw)
{
//priv_ro = ro; /* error, priv_ro is const */
priv_rw = rw; /* ok */
}
};

sample.cc

#include "sample.h"
Sample Sample::global_rw(8,3);
const Sample Sample::global_ro(5,2);

main.cc

#include <iostream>
#include "sample.h"
#define P(x) do{ std::cout << #x " = " << x << std::endl; } while(0)
int main()
{
P(Sample::global_rw.pub_ro_ro);
P(Sample::global_rw.pub_rw_ro);
P(Sample::global_rw.pub_rw_rw);
P(Sample::global_ro.pub_ro_ro);
P(Sample::global_ro.pub_rw_ro);
P(Sample::global_ro.pub_rw_rw);
// Sample::global_ro.pub_ro_ro = 0; /* cannot assign. */
// Sample::global_ro.pub_rw_ro = 0; /* cannot assign. */
// Sample::global_ro.pub_rw_rw = 0; /* cannot assign. */
// Sample::global_rw.pub_ro_ro = 0; /* cannot assign. */
// Sample::global_rw.pub_rw_ro = 0; /* cannot assign. */
Sample::global_rw.pub_rw_rw = 0;
P(Sample::global_rw.pub_ro_ro);
P(Sample::global_rw.pub_rw_ro);
P(Sample::global_rw.pub_rw_rw);
P(Sample::global_ro.pub_ro_ro);
P(Sample::global_ro.pub_rw_ro);
P(Sample::global_ro.pub_rw_rw);
}

生成文件

targets=sample
sample_objs=sample.o main.o
sample: $(sample_objs)
$(CXX) $(LDFLAGS) -o $@ $(sample_objs)

如果取消注释注释行,您将看到不可访问字段或const定义错误。