获取(指向)调用对象

时间:2010-11-25 11:11:04

标签: c++ copy-constructor

我有一个对象 - 一个调度程序类 - 。该调度程序类被赋予成员函数指针,times和指向创建调度程序的对象的指针 这意味着我可以做一些事情:(pObject->*h.function)(*h.param);其中pObject是指向原始对象的指针,ha类包含function + void指针parameter所以我可以将参数传递给原始功能。
当我想初始化这个对象时,我有explicit Scheduler(pObjType o);构造函数(其中pObjType是模板参数)。

当我创建一个应该有此警报的对象时,我输入:

struct A {
    typedef void (A::*A_FN2)(void*);
    typedef Scheduler<A*,A_FN2> AlarmType;
    A(int _x) : alarm(NULL)
    {
        alarm.SetObject(this);
    }
    AlarmType alarm

然而,这种警报类型对对象施加了很大的限制:如果我忘记添加一个拷贝构造函数(到 A ),该类会得到未定义的行为。调度程序将继续指向原始对象,并且原始对象可能超出范围,或者更糟糕的是,可能不会。

有没有一种方法可以在我复制警报时(默认情况下,在调度程序的复制构造函数中)我可以获取调用对象(以及指向它的指针?)? 或者,如果这是不可能的,如果我忘记为我的结构实现一个拷贝构造函数,是否可能抛出(编译)错误? - 并尝试在某处复制此结构?

3 个答案:

答案 0 :(得分:1)

在我看来,你有机会在这里改进你的设计,这可以帮助你摆脱烦恼。

  1. 传递通常是一个坏主意 围绕成员函数指针。它 最好是你的结构 继承自抽象基类, 制作你想要的功能 自定义抽象虚拟。
  2. 如果您不需要复制,最好 在基类中禁止它。 通过使复制构造函数和运算符未定义和私有, 或继承boost::NonCopyable

答案 1 :(得分:0)

如果你想要任何类型的自动复制构造语义,那么你将需要转到CRTP-没有其他模式提供指向拥有对象的指针。

另一件事是你应该使用boost :: / std :: function&lt;&gt;,它们更通用,如果你想能够使用Lua函数,你将需要它。

答案 2 :(得分:0)

阻止您提出的具体问题的最简单方法是使Scheduler不可复制(例如使用boost::noncopyable)。这意味着包含类型Scheduler的值成员的任何客户端类都将无法复制。希望是这为程序员提供了一个提示,可以检查文档并找出Scheduler的复制语义(即为每个新的Scheduler构建一个新的A),但这是可能的如果他们只是通过指针按住Scheduler来解决问题,那么就会出错。对指针进行别名会产生与构造保存指针的Scheduler实例的default-copy-construct完全相同的问题。

每当你有原始指针时,你必须有一个关于对象生命周期的策略。您希望确保任何类A的生命周期至少与Scheduler的相应实例一样长,并且我认为有三种方法可以确保这一点:

  • 使用合成 - 在这种情况下不可能,因为A包含Scheduler,因此Scheduler不能包含A
  • 使用继承,例如以奇怪的重复模板模式(CRTP)的形式
  • 制定一项关于执行A个实例生命周期的全局政策,例如要求它们始终由智能指针保持,或者清理它们是某些类的责任,这些类也知道清理依赖它们的Scheduler

CRTP可以像这样工作:

#include <iostream>

using namespace std;

template<typename T>
struct Scheduler {
    typedef void (T::* MemFuncPtr)(void);

Scheduler(MemFuncPtr action) : 
    action(action)
    {
    }

  private:
    void doAction()
    {
        this->*action();
    }

    MemFuncPtr action;
};

struct Alarm : private Scheduler<Alarm> {
    Alarm() : Scheduler<Alarm>(&Alarm::doStuff)
    {
    }

    void doStuff()
    {
        cout << "Doing stuff" << endl;
    }
};

请注意,私有继承可确保Alarm类的客户端无法将其视为原始Scheduler