在运行时更改对象的行为

时间:2023-01-28 14:37:40

How can be changed the behavior of an object at runtime? (using C++)

如何在运行时更改对象的行为? (使用C ++)

I will give a simple example. I have a class Operator that contains a method operate. Let’s suppose it looks like this:

我将举一个简单的例子。我有一个包含方法操作的类操作符。我们假设它看起来像这样:

double operate(double a, double b){
  return 0.0;
}

The user will give some input values for a and b, and will choose what operation to perform let’s say that he can choose to compute addition or multiplication. Given it’s input all I am allowed to do is instantiate Operator and call operate(a, b), which is written exactly how I mentioned before.

用户将为a和b提供一些输入值,并将选择要执行的操作,假设他可以选择计算加法或乘法。鉴于它的输入,我被允许做的只是实例化运算符和调用操作(a,b),这完全是我之前提到过的。

The methods that compute multiplication or addition will be implemented somewhere (no idea where).

计算乘法或加法的方法将在某处实现(不知道在哪里)。

In conclusion I have to change the behavior of my Operator object depending on the user's input.

总之,我必须根据用户的输入更改Operator对象的行为。

11 个解决方案

#1


9  

The standard pattern for this is to make the outer class have a pointer to an "implementation" class.

这样做的标准模式是使外部类具有指向“实现”类的指针。

// derive multiple implementations from this:
class Implementation
{
    virtual ~Implementation() {} // probably essential!

    virtual void foo() = 0;
};

class Switcheroo
{
    Implementation *impl_;

public:
    // constructor, destructor, copy constructor, assignment 
    // must all be properly defined (any that you can't define, 
    // make private)

    void foo()
    {
        impl_->foo();
    }
};

By forwarding all the member functions of Switcheroo to the impl_ member, you get the ability to switch in a different implementation whenever you need to.

通过将Switcheroo的所有成员函数转发给impl_成员,您可以在需要时切换到不同的实现。

There are various names for this pattern: Pimpl (short for "private implementation"), Smart Reference (as opposed to Smart Pointer, due to the fowarding member functions), and it has something in common with the Proxy and Bridge patterns.

这种模式有多种名称:Pimpl(“私有实现”的缩写),智能参考(与智能指针相反,由于前导成员功能),它与代理和桥接模式有一些共同之处。

#2


6  

I'm mentioning this only as trivia and can't unrecommend it more, but here we go...

我只是把它作为琐事提到,不能再多说一点,但我们在这里......

WARNING DANGER!!!

A stupid trick I've seen is called clutching, I think, but it's only for the truely foolish. Basically you swap the virtualtable pointer to that of another class, it works, but it could theoretically destroy the world or cause some other undefined behavior :)

我认为,我看到的一个愚蠢的伎俩叫做抓紧,但这只是真正的愚蠢。基本上你将virtualtable指针交换到另一个类的指针,它可以工作,但它理论上可以摧毁世界或导致一些其他未定义的行为:)

Anyways instead of this just use dynamic classing and kosher C++, but as an experiment the above is kind of fun...

不管怎么说,只使用动态分类和kosher C ++,但作为一个实验,上面是有趣的...

#3


3  

Coplien's Envelope/Letter Pattern (in his must read book Advanced C++ Programming Styles and Idioms) is the classic way to do this.

Coplien的信封/字母模式(在他必读的高级C ++编程风格和习语中)是经典的方法。

Briefly, an Envelope and a Letter are both subclasses of an abstract base class/interfcae that defines the public interface for all subclasses.

简而言之,Envelope和Letter都是抽象基类/ interfcae的子类,它定义了所有子类的公共接口。

An Envelope holds (and hides the true type of) a Letter.

信封持有(并隐藏真实类型)一封信。

A variety of Letter classes have different implementations of the abstract class's public interface.

各种Letter类具有抽象类的公共接口的不同实现。

An Envelope has no real implementation; it just forards (delegates) to its Letter. It holds a pointer to the abstract base class, and points that at a concrete Letter class instance. As the implementation needs to be changed, the type of Letter subclass pointer to is changed.

信封没有真正的实施;它只是对其信件的代表(代表)。它包含一个指向抽象基类的指针,并指向具体的Letter类实例。由于需要更改实现,因此更改了Letter子类指针的类型。

As users only have a reference to the Envelope, this change is invisible to them except in that the Envelope's behavior changes.

由于用户只有对Envelope的引用,因此除了Envelope的行为更改之外,此更改对于它们是不可见的。

Coplien's examples are particularly clean, because it's the Letters, not the envelope that cause the change.

Coplien的例子特别干净,因为它是字母,而不是引起变化的信封。

One example is of a Number class hierarchy. The abstract base declares certain operations over all Numbers, e.g, addition. Integer and a Complex are examples of concrete subclasses.

一个例子是Number类层次结构。抽象基数声明对所有数字的某些操作,例如,加法。 Integer和Complex是具体子类的示例。

Adding an Integer and an Integer results in an Integer, but adding a Interget and a Complex results in a Complex.

添加整数和整数会产生整数,但添加一个Interget和一个Complex会产生一个Complex。

Here's what the Envelope looks like for addition:

以下是Envelope的添加内容:

public class Number {
  Number* add( const Number* const n ) ; // abstract, deriveds override
}

public class Envelope : public Number {
  private Number* letter;

...

  Number* add( const Number& rhs) { // add a number to this
    // if letter and rhs are both Integers, letter->add returns an Integer
    // if letter is a a Complex, or rhs is, what comes back is a Complex
    //
    letter = letter->add( rhs ) ) ;
    return this;
  }
}

Now in the client's pointer never changes, and they never ever need to know what the Envelop is holding. Here's the client code:

现在客户端的指针永远不会改变,他们永远不需要知道Envelop持有什么。这是客户端代码:

int main() {
  // makeInteger news up the Envelope, and returns a pointer to it
  Number* i = makeInteger( 1 ) ;  
  // makeComplex is similar, both return Envelopes.
  Number* c = makeComplex( 1, 1 ) ;

  // add c to i
  i->add(c) ;


  // to this code, i is now, for all intents and purposes, a Complex!
  // even though i still points to the same Envelope, because 
  // the envelope internally points to a Complex.
}

In his book, Coplien goes into greater depth -- you'll note that the add method requires multi-dispatch of some form --, and adds syntactic sugar. But this is the gist of how you can get what's called "runtime polymorphism".

在他的书中,Coplien进一步深入 - 你会注意到add方法需要多次发送某种形式 - 并添加语法糖。但这是你如何获得所谓的“运行时多态性”的要点。

#4


2  

You can achieve it through dynamic binding (polymorphism)... but it all depends on what you are actually trying to achieve.

你可以通过动态绑定(多态)实现它......但这一切都取决于你实际想要实现的目标。

#5


2  

You can't change the behavior of arbitrary objects using any sane way unless the object was intended to use 'plugin' behaviour through some technique (composition, callbacks etc).

您无法使用任何理智的方式更改任意对象的行为,除非该对象旨在通过某种技术(组合,回调等)使用“插件”行为。

(Insane ways might be overwriting process memory where the function code lies...)

(疯狂的方式可能会覆盖功能代码所在的进程内存......)

However, you can overwrite an object's behavior that lies in virtual methods by overwriting the vtable (An approach can be found in this article ) without overwriting memory in executable pages. But this still is not a very sane way to do it and it bears multiple security risks.

但是,您可以通过覆盖vtable来覆盖虚拟方法中的对象行为(可以在本文中找到方法),而不会覆盖可执行页面中的内存。但这仍然不是一个非常理智的方式,它承担着多重安全风险。

The safest thing to do is to change the behavior of objects that were designed to be changed by providing the appropriate hooks (callbacks, composition ...).

最安全的做法是通过提供适当的钩子(回调,组合......)来改变设计为要改变的对象的行为。

#6


1  

Objects always have the behaviour that's defined by their class.

对象始终具有由其类定义的行为。

If you need different behaviour, you need a different class...

如果你需要不同的行为,你需要一个不同的类......

#7


1  

You could also consider the Role Pattern with dynamic binding..i'm struggling with the same thing that you do..I read about the Strategy pattern but the role one sounds like a good solution also...

你也可以考虑角色模式与动态绑定..我正在努力与你做同样的事情..我读到了战略模式,但角色听起来也是一个很好的解决方案......

#8


0  

There are many ways to do this proxying, pImpl idiom, polymorphism, all with pros and cons. The solution that is best for you will depend on exactly which problem you are trying to solve.

有许多方法可以实现这种代理,pImpl习语,多态,所有这些都有利有弊。最适合您的解决方案将取决于您要解决的问题。

#9


0  

Many many ways:

有很多方法:

Try if at first. You can always change the behavior with if statement. Then you probably find the 'polymorphism' way more accurate, but it depends on your task.

一开始就试试吧。您始终可以使用if语句更改行为。然后你可能会发现'多态“的方式更准确,但这取决于你的任务。

#10


0  

Create a abstract class, declaring the methods, which behavior must be variable, as virtual. Create concrete classes, that will implement the virtual methods. There are many ways to achieve this, using design patterns.

创建一个抽象类,将哪些行为必须是可变的方法声明为虚拟。创建将实现虚方法的具体类。使用设计模式有很多方法可以实现这一目标。

#11


0  

You can change the object behavior using dynamic binding. The design patterns like Decorator, Strategy would actually help you to realize the same.

您可以使用动态绑定更改对象行为。像Decorator,Strategy这样的设计模式实际上会帮助你实现同样的目标。

#1


9  

The standard pattern for this is to make the outer class have a pointer to an "implementation" class.

这样做的标准模式是使外部类具有指向“实现”类的指针。

// derive multiple implementations from this:
class Implementation
{
    virtual ~Implementation() {} // probably essential!

    virtual void foo() = 0;
};

class Switcheroo
{
    Implementation *impl_;

public:
    // constructor, destructor, copy constructor, assignment 
    // must all be properly defined (any that you can't define, 
    // make private)

    void foo()
    {
        impl_->foo();
    }
};

By forwarding all the member functions of Switcheroo to the impl_ member, you get the ability to switch in a different implementation whenever you need to.

通过将Switcheroo的所有成员函数转发给impl_成员,您可以在需要时切换到不同的实现。

There are various names for this pattern: Pimpl (short for "private implementation"), Smart Reference (as opposed to Smart Pointer, due to the fowarding member functions), and it has something in common with the Proxy and Bridge patterns.

这种模式有多种名称:Pimpl(“私有实现”的缩写),智能参考(与智能指针相反,由于前导成员功能),它与代理和桥接模式有一些共同之处。

#2


6  

I'm mentioning this only as trivia and can't unrecommend it more, but here we go...

我只是把它作为琐事提到,不能再多说一点,但我们在这里......

WARNING DANGER!!!

A stupid trick I've seen is called clutching, I think, but it's only for the truely foolish. Basically you swap the virtualtable pointer to that of another class, it works, but it could theoretically destroy the world or cause some other undefined behavior :)

我认为,我看到的一个愚蠢的伎俩叫做抓紧,但这只是真正的愚蠢。基本上你将virtualtable指针交换到另一个类的指针,它可以工作,但它理论上可以摧毁世界或导致一些其他未定义的行为:)

Anyways instead of this just use dynamic classing and kosher C++, but as an experiment the above is kind of fun...

不管怎么说,只使用动态分类和kosher C ++,但作为一个实验,上面是有趣的...

#3


3  

Coplien's Envelope/Letter Pattern (in his must read book Advanced C++ Programming Styles and Idioms) is the classic way to do this.

Coplien的信封/字母模式(在他必读的高级C ++编程风格和习语中)是经典的方法。

Briefly, an Envelope and a Letter are both subclasses of an abstract base class/interfcae that defines the public interface for all subclasses.

简而言之,Envelope和Letter都是抽象基类/ interfcae的子类,它定义了所有子类的公共接口。

An Envelope holds (and hides the true type of) a Letter.

信封持有(并隐藏真实类型)一封信。

A variety of Letter classes have different implementations of the abstract class's public interface.

各种Letter类具有抽象类的公共接口的不同实现。

An Envelope has no real implementation; it just forards (delegates) to its Letter. It holds a pointer to the abstract base class, and points that at a concrete Letter class instance. As the implementation needs to be changed, the type of Letter subclass pointer to is changed.

信封没有真正的实施;它只是对其信件的代表(代表)。它包含一个指向抽象基类的指针,并指向具体的Letter类实例。由于需要更改实现,因此更改了Letter子类指针的类型。

As users only have a reference to the Envelope, this change is invisible to them except in that the Envelope's behavior changes.

由于用户只有对Envelope的引用,因此除了Envelope的行为更改之外,此更改对于它们是不可见的。

Coplien's examples are particularly clean, because it's the Letters, not the envelope that cause the change.

Coplien的例子特别干净,因为它是字母,而不是引起变化的信封。

One example is of a Number class hierarchy. The abstract base declares certain operations over all Numbers, e.g, addition. Integer and a Complex are examples of concrete subclasses.

一个例子是Number类层次结构。抽象基数声明对所有数字的某些操作,例如,加法。 Integer和Complex是具体子类的示例。

Adding an Integer and an Integer results in an Integer, but adding a Interget and a Complex results in a Complex.

添加整数和整数会产生整数,但添加一个Interget和一个Complex会产生一个Complex。

Here's what the Envelope looks like for addition:

以下是Envelope的添加内容:

public class Number {
  Number* add( const Number* const n ) ; // abstract, deriveds override
}

public class Envelope : public Number {
  private Number* letter;

...

  Number* add( const Number& rhs) { // add a number to this
    // if letter and rhs are both Integers, letter->add returns an Integer
    // if letter is a a Complex, or rhs is, what comes back is a Complex
    //
    letter = letter->add( rhs ) ) ;
    return this;
  }
}

Now in the client's pointer never changes, and they never ever need to know what the Envelop is holding. Here's the client code:

现在客户端的指针永远不会改变,他们永远不需要知道Envelop持有什么。这是客户端代码:

int main() {
  // makeInteger news up the Envelope, and returns a pointer to it
  Number* i = makeInteger( 1 ) ;  
  // makeComplex is similar, both return Envelopes.
  Number* c = makeComplex( 1, 1 ) ;

  // add c to i
  i->add(c) ;


  // to this code, i is now, for all intents and purposes, a Complex!
  // even though i still points to the same Envelope, because 
  // the envelope internally points to a Complex.
}

In his book, Coplien goes into greater depth -- you'll note that the add method requires multi-dispatch of some form --, and adds syntactic sugar. But this is the gist of how you can get what's called "runtime polymorphism".

在他的书中,Coplien进一步深入 - 你会注意到add方法需要多次发送某种形式 - 并添加语法糖。但这是你如何获得所谓的“运行时多态性”的要点。

#4


2  

You can achieve it through dynamic binding (polymorphism)... but it all depends on what you are actually trying to achieve.

你可以通过动态绑定(多态)实现它......但这一切都取决于你实际想要实现的目标。

#5


2  

You can't change the behavior of arbitrary objects using any sane way unless the object was intended to use 'plugin' behaviour through some technique (composition, callbacks etc).

您无法使用任何理智的方式更改任意对象的行为,除非该对象旨在通过某种技术(组合,回调等)使用“插件”行为。

(Insane ways might be overwriting process memory where the function code lies...)

(疯狂的方式可能会覆盖功能代码所在的进程内存......)

However, you can overwrite an object's behavior that lies in virtual methods by overwriting the vtable (An approach can be found in this article ) without overwriting memory in executable pages. But this still is not a very sane way to do it and it bears multiple security risks.

但是,您可以通过覆盖vtable来覆盖虚拟方法中的对象行为(可以在本文中找到方法),而不会覆盖可执行页面中的内存。但这仍然不是一个非常理智的方式,它承担着多重安全风险。

The safest thing to do is to change the behavior of objects that were designed to be changed by providing the appropriate hooks (callbacks, composition ...).

最安全的做法是通过提供适当的钩子(回调,组合......)来改变设计为要改变的对象的行为。

#6


1  

Objects always have the behaviour that's defined by their class.

对象始终具有由其类定义的行为。

If you need different behaviour, you need a different class...

如果你需要不同的行为,你需要一个不同的类......

#7


1  

You could also consider the Role Pattern with dynamic binding..i'm struggling with the same thing that you do..I read about the Strategy pattern but the role one sounds like a good solution also...

你也可以考虑角色模式与动态绑定..我正在努力与你做同样的事情..我读到了战略模式,但角色听起来也是一个很好的解决方案......

#8


0  

There are many ways to do this proxying, pImpl idiom, polymorphism, all with pros and cons. The solution that is best for you will depend on exactly which problem you are trying to solve.

有许多方法可以实现这种代理,pImpl习语,多态,所有这些都有利有弊。最适合您的解决方案将取决于您要解决的问题。

#9


0  

Many many ways:

有很多方法:

Try if at first. You can always change the behavior with if statement. Then you probably find the 'polymorphism' way more accurate, but it depends on your task.

一开始就试试吧。您始终可以使用if语句更改行为。然后你可能会发现'多态“的方式更准确,但这取决于你的任务。

#10


0  

Create a abstract class, declaring the methods, which behavior must be variable, as virtual. Create concrete classes, that will implement the virtual methods. There are many ways to achieve this, using design patterns.

创建一个抽象类,将哪些行为必须是可变的方法声明为虚拟。创建将实现虚方法的具体类。使用设计模式有很多方法可以实现这一目标。

#11


0  

You can change the object behavior using dynamic binding. The design patterns like Decorator, Strategy would actually help you to realize the same.

您可以使用动态绑定更改对象行为。像Decorator,Strategy这样的设计模式实际上会帮助你实现同样的目标。