假设我有一个RAII风格的C ++类:
class StateSaver
{
public:
StateSaver(int i) { saveState(); }
~StateSaver() { restoreState(); }
};
...在我的代码中使用如下:
void Manipulate()
{
StateSaver save(1);
// ...do stuff that modifies state
}
......目标是进入一些状态,做一些事情,然后离开那个范围时离开那个状态。有没有办法让这个拼写错误无法编译(或警告,或以某种方式抱怨,以便可以注意到错误)?
void Manipulate()
{
StateSaver(1); // ruh-roh, state saved and immediately restored!
// ...do stuff that modifies state
}
我不知道C ++本身有什么东西可以用来防止这种情况,但这并不意味着它不存在。如果C ++中没有任何内容,则可以接受特定于编译器的扩展。我主要对gcc和msvc的任何目标感兴趣(有一天icc,其他编译器的想法欢迎,但不太可能有用)所以任何一个hacks都很有用(当然,抽象为适当的#ifdef'd宏定义)
答案 0 :(得分:10)
我不确定在编译时是否可以执行任何操作。对于运行时检查,您可以这样做:
struct SaveMatrix
{
SaveMatrix(const SaveMatrix& that) {
assert(this == &that);
glPushMatrix();
}
~SaveMatrix() { glPopMatrix(); }
};
这需要客户写信:
SaveMatrix sm(sm);
并且没有办法对临时做同样的事情而不将它绑定到标识符(此时它与自动变量没有区别)。
答案 1 :(得分:6)
SaveMatrix save();
也没有定义对象。它声明了一个函数。
你几乎无法阻止别人(或你自己,FTM)做他们想做的事情。我唯一能想到的不是编写代码本身,而是编写宏代码。
#define SAVE_MATRIX SaveMatrix save ## __LINE__
然而,这非常难看。 OTOH,它确实在编译时捕获了错误。
答案 2 :(得分:3)
我实际上不得不从Waldo发布的变种中以一系列方式调整my solution,但我最终得到的是一个宏观版本:
class GuardNotifier
{
bool* notified;
public:
GuardNotifier() : notified(NULL) { }
void init(bool* ptr) { notified = ptr; }
~GuardNotifier() { *notified = true; }
};
class GuardNotifyReceiver
{
bool notified;
public:
GuardNotifyReceiver() : notified(false) { }
void init(const GuardNotifier& notifier)
{ const_cast<GuardNotifier&>(notifier).init(¬ified); }
~GuardNotifyReceiver() { assert(notified); }
};
class StateSaver
{
GuardNotifyReceiver receiver;
public:
StateSaver(int i,
const GuardNotifier& notifier = GuardNotifier())
{
receiver.init(notifier)
saveState();
}
~StateSaver()
{
restoreState();
}
};
答案 3 :(得分:1)
该类永远无法判断它是否被实例化为临时(SaveMatrix())或变量(SaveMatrix save;)。我认为阻止程序员在没有堆栈或宏hack的情况下执行该操作的最佳方法是在构造后强制执行成员函数调用,例如:
class RAII
{
public:
bool valid;
RAII()
: valid(false)
{
cout << "RAII ctor" << endl;
}
void Do()
{
valid = true;
}
~RAII()
{
assert(valid);
cout << "RAII dtor" << endl;
}
};
然后按以下方式工作:
{
// Intended use
RAII raii;
raii.Do();
cout << "Some task" << endl;
}
{
// Woops: forgot Do()
RAII raii;
cout << "Some task" << endl;
}
{
// Woops: forgot Do()
RAII();
cout << "Some task" << endl;
}
{
// Programmer shot self in foot, hopefully the act of typing this would make them realise that
RAII().Do();
cout << "Some task" << endl;
}