在没有指针的情况下在 C++ 中实现策略模式
Implement Strategy Pattern in C++ without Pointers
我的目标是拥有 Strategy Pattern 的 C++ 实现,无需手动分配内存。从概念上讲,我觉得在我给出的例子中没有必要这样做。此外,手动内存管理容易出错。请记住,我的示例只是一个 MWE,实际上所有对象都更加复杂,手动内存管理经常会引入错误。
在下面的例子中,class Base
需要被赋予一个 Strategy
来构建,它将用于在其方法 [=13= 中产生输出].当然也可以不给一个Strategy
,因为那个class是抽象的,所以这个论点必须是引用。由于我的上述原因,它是 而不是 指针。
下面的代码可以编译,但是只能因为const
已经在几个地方添加了。
class Strategy {
public:
virtual int decision() const = 0;
};
class AlwaysZero : public Strategy {
public:
int decision() const { return 0; }
};
class Base {
private:
Strategy const &strategy;
public:
Base(Strategy const &s) : strategy(s) {}
int getStrategyDecision() {
return strategy.decision();
}
};
class Main : public Base {
public:
Main() : Base(AlwaysZero()) {}
};
int main ()
{
Main invoker;
return invoker.getStrategyDecision();
}
如果删除 const
限定符,它会失败,因为在 Main
的构造函数中,将临时对象 AlwaysZero()
传递给 [=11= 的构造函数的引用参数] 是非法的。
我能理解那个错误,但是
- 为什么当一切都是
const
时它仍然有效?这似乎表明它应该在没有 const
的情况下同样有效,我只是遗漏了一些东西。
- 我什至不想要一个“temporary”对象,我必须这样做的唯一原因是因为我不能传递
Strategy
本身,因为 class 是抽象的。
同样,我知道我可以通过使变量 strategy
成为指向 Strategy
的指针并将 new AlwaysZero()
传递给 Base
构造函数来解决这个问题。如上所述,这是我不想要的。
我的问题是双重的。首先,我想知道为什么我的示例在没有 const
的情况下无法编译。我想了解这里发生了什么。
其次,我想知道是否可以修改此示例,使 strategy
不再是 const
(并且有效),但不使用任何类型的指针。只有当第二个问题的答案是 "no" 时,我才会开始寻找巧妙的方法来避免这个问题(例如评论中建议的智能指针)。
PS:我使用 g++ 作为编译器。这可能重要也可能不重要。
您的代码可以编译的原因是 const 引用可以延长临时文件的寿命,但非 const 引用不会。有关详细说明,请参阅 https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/。
至于为什么 C++ 标准说非常量引用不会延长临时对象的生命周期。正如 here, non-const references can't be bound to temporaries in the first place. See here 所解释的一个简单的原因示例。
所以,你的技术应该没问题。但是如果你想要一些其他的选择...
策略模板
template<typename T>
class Base {
private:
T strategy;
public:
Base(const T& s) : strategy(s) {}
int getStrategyDecision() {
return strategy.decision();
}
};
class Main : public Base<AlwaysZero> {
public:
Main() : Base(AlwaysZero()) {}
};
非拥有指针
无需手动内存管理即可使用指针。只要它们是非拥有指针。
class Base {
private:
Strategy *strategy;
protected:
void setStrategy(Strategy& s) { strategy = &s; }
public:
int getStrategyDecision() {
return strategy->decision();
}
};
class Main : public Base {
AlwaysZero my_strategy;
public:
Main() { setStrategy(my_strategy); }
};
我的目标是拥有 Strategy Pattern 的 C++ 实现,无需手动分配内存。从概念上讲,我觉得在我给出的例子中没有必要这样做。此外,手动内存管理容易出错。请记住,我的示例只是一个 MWE,实际上所有对象都更加复杂,手动内存管理经常会引入错误。
在下面的例子中,class Base
需要被赋予一个 Strategy
来构建,它将用于在其方法 [=13= 中产生输出].当然也可以不给一个Strategy
,因为那个class是抽象的,所以这个论点必须是引用。由于我的上述原因,它是 而不是 指针。
下面的代码可以编译,但是只能因为const
已经在几个地方添加了。
class Strategy {
public:
virtual int decision() const = 0;
};
class AlwaysZero : public Strategy {
public:
int decision() const { return 0; }
};
class Base {
private:
Strategy const &strategy;
public:
Base(Strategy const &s) : strategy(s) {}
int getStrategyDecision() {
return strategy.decision();
}
};
class Main : public Base {
public:
Main() : Base(AlwaysZero()) {}
};
int main ()
{
Main invoker;
return invoker.getStrategyDecision();
}
如果删除 const
限定符,它会失败,因为在 Main
的构造函数中,将临时对象 AlwaysZero()
传递给 [=11= 的构造函数的引用参数] 是非法的。
我能理解那个错误,但是
- 为什么当一切都是
const
时它仍然有效?这似乎表明它应该在没有const
的情况下同样有效,我只是遗漏了一些东西。 - 我什至不想要一个“temporary”对象,我必须这样做的唯一原因是因为我不能传递
Strategy
本身,因为 class 是抽象的。
同样,我知道我可以通过使变量 strategy
成为指向 Strategy
的指针并将 new AlwaysZero()
传递给 Base
构造函数来解决这个问题。如上所述,这是我不想要的。
我的问题是双重的。首先,我想知道为什么我的示例在没有 const
的情况下无法编译。我想了解这里发生了什么。
其次,我想知道是否可以修改此示例,使 strategy
不再是 const
(并且有效),但不使用任何类型的指针。只有当第二个问题的答案是 "no" 时,我才会开始寻找巧妙的方法来避免这个问题(例如评论中建议的智能指针)。
PS:我使用 g++ 作为编译器。这可能重要也可能不重要。
您的代码可以编译的原因是 const 引用可以延长临时文件的寿命,但非 const 引用不会。有关详细说明,请参阅 https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/。
至于为什么 C++ 标准说非常量引用不会延长临时对象的生命周期。正如 here, non-const references can't be bound to temporaries in the first place. See here 所解释的一个简单的原因示例。
所以,你的技术应该没问题。但是如果你想要一些其他的选择...
策略模板
template<typename T>
class Base {
private:
T strategy;
public:
Base(const T& s) : strategy(s) {}
int getStrategyDecision() {
return strategy.decision();
}
};
class Main : public Base<AlwaysZero> {
public:
Main() : Base(AlwaysZero()) {}
};
非拥有指针
无需手动内存管理即可使用指针。只要它们是非拥有指针。
class Base {
private:
Strategy *strategy;
protected:
void setStrategy(Strategy& s) { strategy = &s; }
public:
int getStrategyDecision() {
return strategy->decision();
}
};
class Main : public Base {
AlwaysZero my_strategy;
public:
Main() { setStrategy(my_strategy); }
};