【问题标题】:How to pass a member function to a base class contructor?如何将成员函数传递给基类构造函数?
【发布时间】:2020-02-25 12:47:13
【问题描述】:

我有一个要使用的类结构,并且我想使用派生类的函数作为构造函数参数传递给基类。我找不到合适的语法(C 的新手 :))

这是我使用的基类,它有一个带有回调函数的构造函数:

class SPortSensor {
        public:
            SPortSensor(sensorData (*pCallback)(SPortSensor*));
            sensorData getValue();
        private:
            sensorData (*getData)(SPortSensor*);
    };

这是实现类内回调函数(pCallback)的派生类,所以它有不同的构造函数和需要传递给基类构造函数的成员函数:

    class SimpleSensor : public SPortSensor {
        public: 
            SimpleSensor(int id);
            long value;
        private:
            int _id;
            sensorData pCallback(SPortSensor*);
    };

这个头文件编译得很好。我看到的唯一错误是 SimpleSensor 构造函数的实现。我找不到正确的语法:

sensorData SimpleSensor::pCallback(SPortSensor* sensor) {
   ...
}

SimpleSensor::SimpleSensor(int id) : SPortSensor(pCallback) {
  _id = id;
}

谷歌搜索这个问题并没有太大帮助,因为我可能没有使用正确的搜索词并且对 c++ 的理解不够(我是 C# 人)。

【问题讨论】:

  • 要调用pCallback 或SimpleSensor,您需要一个SimpleSensor 实例来调用它,还需要一个SPortSensor* 作为参数。 SPortSensor 的内部如何获得第一个?
  • 看起来更像是多态性的工作。
  • 你能说明你打算用回调函数做什么吗?似乎有一个非常简单的解决方案不涉及手动将您自己的函数传递给您的父类。另外,您使用的是什么 C++ 版本/标准(在 C++11 之前或之后)?
  • 我正在使用 c++11(平台 IO / Arduino)。基本上 SPortSensor 可以正常工作。它需要来自使用此类的应用程序的回调函数,因此必须强制使用构造函数。我现在有一个“内部”具有回调函数的简单版本,但我想重新使用 SPortSensor 类,因为它包含更多功能并被其他类使用。
  • 虚函数有用吗? (它不能在基类构造函数中使用,但如果你需要它,无论你如何设置都会有问题,因为派生类的成员尚未初始化。)

标签: c++ function constructor member derived-class


【解决方案1】:

循环引用闻起来可能存在一些一般性的所有权问题。

但是,解决僵局的一种方法是使用std::function<sensorData(SPortSensor*)> 而不是函数指针。然后用指向自身的 lambda 初始化基类:

SimpleSensor(int id) : SPortSensor([this](SPortSensor* sps) { return pCallback(sps); }) { }

我不能有意识地宽恕这个,但是当我尝试它时它并没有爆炸。需要注意的是,SimpleSensor 在调用基本构造函数时不会被初始化,因此 SPortSensor 不能在自己的构造函数中调用 lambda。

【讨论】:

  • 如何替换成员 sensorData (getData)(SPortSensor);为此?
  • std::function<sensorData(SPortSensor*)> getData;
  • 不喜欢这样:限定名是不允许的。
  • 这是我测试的:godbolt.org/z/2tR9Qi 不过出于好奇,您是否尝试在此处复制 C# 功能?
  • @parktomatomi 他正在使用 Arduino,所以很有可能他没有基于 ARM 的,因此没有 std::function 等等。但在问题中没有提到这一点,而是在后面的 cmets 中......
【解决方案2】:

回调通常发生在不同的对象之间。一种解决方案是在构造函数之后区分派生类方法的使用(例如在 init 方法中?),并将该方法声明为虚拟。

#include <iostream>

struct sensorData {
    double val = 0;
};

class SPortSensor {
public:
    virtual sensorData getValue() {
        std::cout << "Base class\n";
        sensorData sd;
        return sd;
    }
};

class SimpleSensor : public SPortSensor {
public: 
    long value;
    SimpleSensor(int id): id(id) {
    }
    virtual sensorData getValue() {
        std::cout << "Derived class\n";
        sensorData sd;
        return sd;
    }
private:
    int id;
};


int main() {
    SPortSensor base;
    SimpleSensor derived(1);
    base.getValue();
    derived.getValue();
    SPortSensor* derivedPtr = new SimpleSensor(2);
    derivedPtr->getValue();
    return 0;
}

【讨论】:

  • 使用多态的想法是合理/好的,但你的代码 sn-p 与所讨论的无关。如果您可以将问题中的代码适应于此,那将更加有用。
  • 是的,而且多态并不是一个真正的选择,因为我的 A 类没有该函数的任何实现。
  • @AProgrammer 代码的重点是表明从构造函数调用虚方法不起作用。这就是为什么有两个调用的原因,我建议 OP 将虚拟方法的使用延迟到构造函数之后。
  • @HermanKruisman 如果你的基类没有实现,那么你应该声明它是纯虚拟的。
  • @MaxLanghof 代码已修改。
【解决方案3】:

所以我认为复杂性(问题?)在于尝试用几行代码来做事。我只是切换到带有抽象类的多态来简化事情。

抽象基础:

    class SPortSensor {
        public:
            void (*valueSend)(void);
            virtual sensorData getData () = 0;
    };

2个实现类:

class CustomSPortSensor : public SPortSensor {
        public:
            CustomSPortSensor(sensorData (*callback)(CustomSPortSensor*));
        private:
            sensorData (*_callback)(CustomSPortSensor*);

        virtual sensorData getData(){
            return _callback(this);
        }
    };

    class SimpleSPortSensor : public SPortSensor {
        public: 
            SimpleSPortSensor(int id);
            long value;
        private:
            int _id;

        virtual sensorData getData(){
            sensorData data;
            data.sensorId = _id;
            data.value = value;
            return data;
        }
    };

现在可以按预期工作,并且非常易于使用和理解(实现的使用必须尽可能可读)。

现在我只希望虚函数的实现在 .cpp 而不是 .h.......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 2023-04-04
    • 2021-12-19
    • 1970-01-01
    相关资源
    最近更新 更多