作为帮助可视化问题的动机(否则只是跳到下面的代码),假设我正在编写一个纸牌游戏,其中某些牌“在场”允许您“购买”手中的其他牌。所以,举个例子,假设你有一手说四张牌,其中包含一张王牌,你决定玩它。然后你得到另外一张四张牌,现在这张牌让你可以免费玩一张Queen卡。等等。
现在,我想编写这个游戏的代码。我创建了一个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?即使没有,那么即便如此,有没有办法让这些对象只是出于兴趣。
答案 0 :(得分:1)
正如您所指出的,卡的数据(其身份加上一些关于它的规则)是不变的。而且,它们在编译时是已知的。所以:
#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}},
};
您可以根据需要调整阵列。现在您拥有一整套数据,这些数据在编译时都是已知的,并且存储紧凑。
答案 1 :(得分:0)
在不违反正确构造规则的情况下,您无法构造相互引用的const
个对象。但是......
您可以静态构造它们为读写,private
,static
类成员,然后发布const
引用。这将使这些成员的访问权限为只读。
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 */
}
};
#include "sample.h"
Sample Sample::global_rw(8,3);
const Sample Sample::global_ro(5,2);
#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
定义错误。