有没有办法只声明一个指向特定方法的指针?

时间:2018-09-05 09:44:49

标签: c++ pointers member-function-pointers

我有一堂课

class Car
{
    public:
        Car();
        ~Car();
        // nr indicates which particular door.
        void startEngie();
        void openDoor(int nr);
        void closeDoor(int nr); 
        void openWindow(int nr);
        void closeWindow(int nr);
        void turnOnAirConditioner(int vloume);
}

现在,void (Car::*action)(int)声明了一个指向action方法的名为Car的指针。问题在于可以为它分配许多方法:&Car::openDoor&Car::closeDoor&Car::openWindow&Car::closeWindow&Car::turnOnAirConditioner,但不能分配&Car::startEngie

我想知道我是否正确:没有办法只声明一个指向特定方法的指针。换句话说:pointer type仅接受特定的命名函数,无法声明。

4 个答案:

答案 0 :(得分:3)

您不能定义 只是“另一种类型的一个特定值”的类型,例如,仅存在的值恰好是&Car::closeDoor的类型。

您可以做的一件事是接触瑞士军刀编程-引入一个间接级别-并定义一个表示 的特定值的类型,方法是将其包装起来里面的东西。

// Simplified Car
class Car
{
public:
    void openDoor(int i) { std::cout << "open door\n"; }
    void closeWindow(int i) { std:cout << "close window\n"; }
};

using Fun = void (Car::*)(int);

// A template for an immutable structure, so it 
// can hold only one specific value; the template argument.
template<Fun fun>
struct CarOperation
{
    const Fun f = fun;
};

// Now we can make one type for each operation
using OpenDoor = CarOperation<&Car::openDoor>;
using CloseWindow = CarOperation<&Car::closeWindow>;

int main()
{
    Car c;
    OpenDoor d;
    (c.*d.f)(1);
    CloseWindow e;
    (c.*e.f)(2);
}

语法有点不幸,因为显然.*运算符不会对其右操作数进行任何隐式转换。

当然,这只是简单地调用类型所指示的函数的一种round回和混淆的方式。

对于可以容纳函数的预定子集的类型(例如DoorOperationOpener类型),这将更为有用。
您可能可以使用聪明的模板编程来构建它,但是我不是一个聪明的模板程序员。

答案 1 :(得分:1)

这种指针void (Car::*action)(int)很少使用。 如今,有三种首选的方法可以解决此问题:

  • 模板参数(例如STL算法)
  • 界面(纯抽象类)
  • std::function<void()>和或lambdas

显然您需要第二种解决方案:

class IWindowOpenable {
public:
    virtual ~IWindowOpenable() {}

    virtual void openWindow(int nr) = 0;
};

class Car : public IWindowOpenable
{
public:
    Car();
    ~Car();

    void startEngie();
    void openDoor(int nr);
    void closeDoor(int nr); 
    void openWindow(int nr);
    void closeWindow(int nr);
    void turnOnAirConditioner(int vloume);
};

Car car;
IWindowOpenable *windowOpener = &Car;

请注意,实现接口IWindowOpenable的所有内容只能在名称为openWindow的方法上运行。 该指针可以指向实现此接口的对象的任何错字。

答案 2 :(得分:1)

在评论中,您说您是在问一个纯粹的学术问题,所以让我给出一个纯粹的学术答案。您不能仅指向一个方法,因为所有这些方法都具有相同的类型。您可以做的是使它们具有不同的类型:

struct Car
{
    struct open_door_tag { };
    struct close_door_tag { };

    void open_left_door(int nr, open_door_tag = {});
    void open_right_door(int nr, open_door_tag = {});
    void close_door(int nr, close_door_tag = {});
};

现在,如果您有指针

void (Car::*open_that_door_ptr)(int, Car::open_door_tag);

您对分配有一些限制:

open_that_door_ptr = &Car::open_left_door;   // OK
open_that_door_ptr = &Car::open_right_door;  // OK
//open_that_door_ptr = &Car::close_door;     // Won't compile

不幸的是(或没有),您不能在没有提供标签的情况下通过此指针调用方法:

Car car;
(car.*open_that_door_ptr)(2, Car::open_door_tag{});
// or (thanks, @Jarod42)
(car.*open_that_door_ptr)(2, {});
// (car.*open_that_door_ptr)(2);     // Won't compile

注意。在我发布此答案后,我看到@ Jarod42的评论,他建议做基本上相同的事情。

答案 3 :(得分:1)

Marek给出了考虑使用更多C ++惯用替代方法的正确建议,尽管您的希望是合理的,并且可以通过简单的bundle install来实现。

可能要这样做的原因与所有其他const定义相同(原则上所有这些都可以用其定义的右侧代替):一个人想要一个符号名称,以便特定的含义值在使用中是可以理解的,并且如果希望更改const值,则需要一个维护点。这就是为什么每个人都不喜欢代码中的整数文字。

这些确切原因适用于constconst int的定义。

因此,举一个人为的例子,如果您想在每次汽车启动时都调用一个函数,作为该特定汽车的非可变属性,则可以声明一个void (Car::*const action)(int)常量成员函数指针作为成员(我也介绍了typedef)。指针必须在汽车的构造函数中初始化,就像所有const成员一样。

atStart

用户代码如下:

#include<iostream>
using namespace std;

struct Car
{
        typedef void (Car::* const CarfuncT)(void);
        Car(CarfuncT atStartArg)
            : atStart(atStartArg) 
        {}
        void startEngine() { cout << "Start Engine\n"; (this->*atStart)(); };
        void turnOnAC() { cout << "AC on\n"; }
        void turnOnHeat() { cout << "Heat on\n"; }
        void turnOnRadio() { cout << "Radio on\n"; }
        void (Car::* const atStart)();
};