在c++中模拟CLOS:before、:after和:around
Emulating CLOS :before, :after, and :around in C++
CLOS有一个简洁的概念:before,:after和:around方法。
- :before方法在主方法之前调用。
- :after方法在主方法之后调用。
- :around方法围绕:before+primary+:after序列调用。
:before、:after和:around方法被链接而不是覆盖。假设父类和子类都定义了foo方法和:before foo方法。子节点的foo方法会覆盖父节点的foo方法,但是在父节点和子节点的:before foo方法被调用之前,这个被覆盖的方法才会被调用。
Python装饰器提供了类似于CLOS:around方法的东西。c++中没有这样的东西。必须手卷:
class Child : public Parent {
virtual void do_something (Elided arguments) {
do_some_preliminary_stuff();
Parent::do_something (arguments);
do_some_followup_stuff();
}
};
缺点:
- 这是一个反模式。
- 它要求我明确指定父类。
- 它要求我的类的扩展器遵循相同的范式。
- 如果我需要调用祖父母,因为父没有覆盖
do_something
,多重继承怎么办? - 它没有完全捕捉CLOS的概念。
当我使用Flavors (CLOS的前身)时,我发现这些概念非常方便。我已经在一些地方使用了上面的解决方法,并且有一些地方将其作为反模式进行了挑战。(其他人已经在其他地方模仿它,所以这种嘲笑并不普遍。)
问题是:这在c++中被认为是一种反模式吗?有没有更好的解决方法?您可以使用(std/boost)::shared_ptr
很好地获得这些基础知识。详见:http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/sp_techniques.html#wrapper
获得你提到的继承行为只需要前缀/后缀函数调用父类中的前缀/后缀函数。
我可以这么做,但是还是有点难看。
基本上我把实际的工作放在一个单独的钩子中,所以你不需要在处理方法中调用pre/post钩子。在继承链中,你可以完全控制是否要添加pre/post钩子,以及钩子调用的顺序(在子钩子之前或之后调用父钩子)。
#include <iostream>
#define C(s) std::cout << s << std::endl;
class Parent {
public:
virtual void do_something(int arg) {
do_some_pre_hook();
do_some_hook(arg);
do_some_post_hook();
}
virtual void do_some_pre_hook() {
C("parent pre_hook");
}
virtual void do_some_post_hook() {
C("parent post_hook");
}
virtual void do_some_hook(int arg) {
//this is where you actually do the work
}
};
class Child : public Parent {
public:
typedef Parent super;
virtual void do_some_pre_hook() {
super::do_some_pre_hook();
C("Child pre_hook");
}
virtual void do_some_post_hook() {
super::do_some_post_hook();
C("Child post_hook");
}
};
class GrandChild : public Child {
public:
typedef Child super;
virtual void do_some_pre_hook() {
super::do_some_pre_hook();
C("GrandChild pre_hook");
}
virtual void do_some_post_hook() {
super::do_some_post_hook();
C("GrandChild post_hook");
}
virtual void do_some_hook(int arg) {
//this is where you actually do the work
C("GrandChild hook");
}
};
int main() {
GrandChild gc;
gc.do_something(12);
}
注意:理想情况下,你会使用AOP c++编译器或编译器扩展来完成这样的任务,但上次我尝试它时,它不是很稳定…
我并不是说这与其他语言所做的相同或可比较,但我认为非虚拟接口习语适用于您的问题:
class parent {
public:
void foo()
{
before_foo();
do_foo();
after_foo();
}
protected:
// you can make those pure virtual with an implentation, too
virtual void before_foo() { ... }
virtual void do_foo() { ... }
virtual void after_foo() { ... }
};
class child: public parent {
protected: // or private
void before_foo() { ... }
void do_foo() { ... }
// must provide a dummy after_foo that delegates to parent::after_foo
// if it is pure virtual in the parent class
};
调用p.foo()
时,总是调用衍生最多的before_foo
、after_foo
和do_foo
。
- 为什么我会收到"Run-Time Check Failure #2 - Stack around the variable 'pr' was corrupted"错误?
- "terminate called after throwing an instance of std::invalid_argument' what(): stoi ?"
- 如何修复初始化后'stack around variable was corrupted.'变量未更改为分配的值
- NDK 构建中的异常"error: expected ';' after top level declarator"
- Playing around with getopt
- 标准::复制失败,"cannot seek vector iterator after end"
- std::bind and stack-use-after-scope
- C++:在字符串数据类型中创建单词"letter after letter"
- std::reference_wrapper around *this
- What value should `std::stringstream::fail()` return after r
- C++ 错误"Braces around scalar initializer for type int*"
- "used after it was moved [bugprone-use-after-move]"警告在这里是一个真正的问题吗?
- GL_INVALID_ENUM right after context initialization
- 如何在 C++ 中继续该函数而不退出 after else 语句
- 缩小 C++11 中的转换范围:"actual value after conversion"是什么?
- "Explicit specialization of std::iterator_traits<char *> after instantiation"(咕)
- Segfault after the return 0;
- 显示"terminate called after throwing an instance of 'std::bad_alloc'"时出错
- Heap corruption after new char[strlen
- 在c++中模拟CLOS:before、:after和:around