Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++;继承和成员函数指针 在C++中,成员函数指针是否可以指向派生的(甚至基类)类成员?_C++_Inheritance_Oop_Member Function Pointers - Fatal编程技术网

C++;继承和成员函数指针 在C++中,成员函数指针是否可以指向派生的(甚至基类)类成员?

C++;继承和成员函数指针 在C++中,成员函数指针是否可以指向派生的(甚至基类)类成员?,c++,inheritance,oop,member-function-pointers,C++,Inheritance,Oop,Member Function Pointers,编辑: 也许举个例子会有所帮助。假设我们有一个按继承顺序排列的三个类的层次结构X,Y,Z。 Y因此有一个基类X和一个派生类Z 现在我们可以为类Y定义成员函数指针p。其内容如下: void (Y::*p)(); (为了简单起见,我假设我们只对签名为void f()的函数感兴趣) 该指针p现在可用于指向类Y的成员函数 void (Y::*p)() = &Z::z_fn; // illegal 这个问题(实际上是两个问题)是: 是否可以使用p指向派生类Z中的函数 是否可以使用p指向基类X中

编辑: 也许举个例子会有所帮助。假设我们有一个按继承顺序排列的三个类的层次结构
X
Y
Z
Y
因此有一个基类
X
和一个派生类
Z

现在我们可以为类
Y
定义成员函数指针
p
。其内容如下:

void (Y::*p)();
(为了简单起见,我假设我们只对签名为
void f()
的函数感兴趣)

该指针
p
现在可用于指向类
Y
的成员函数

void (Y::*p)() = &Z::z_fn; // illegal
这个问题(实际上是两个问题)是:

  • 是否可以使用
    p
    指向派生类
    Z
    中的函数
  • 是否可以使用
    p
    指向基类
    X
    中的函数

  • 我相信是这样。由于函数指针使用签名来标识自身,因此基本/派生行为将依赖于您调用它的任何对象。

    您可能想查看本文。在某些情况下,简短的回答似乎是肯定的。

    我不确定您要问什么,但下面是一个适用于虚拟函数的示例:

    #include <iostream>
    using namespace std;
    
    class A { 
    public:
        virtual void foo() { cout << "A::foo\n"; }
    };
    class B : public A {
    public:
        virtual void foo() { cout << "B::foo\n"; }
    };
    
    int main()
    {
        void (A::*bar)() = &A::foo;
        (A().*bar)();
        (B().*bar)();
        return 0;
    }
    
    #包括
    使用名称空间std;
    A类{
    公众:
    
    虚拟void foo(){cout我的实验揭示了以下几点:警告-这可能是未定义的行为。如果有人能提供一个明确的参考,这将是很有帮助的

  • 这是可行的,但在将派生成员函数分配给
    p
    时需要强制转换
  • 这同样有效,但在取消引用
    p
    时需要额外的强制转换
  • 如果我们真的感到雄心勃勃,我们可以问
    p
    是否可以用来指向不相关类的成员函数。我没有尝试过,但dagorym答案中链接的页面表明这是可能的

    总之,我将尽量避免以这种方式使用成员函数指针。下面这样的段落不会激发信心:

    成员函数之间的转换 指针是一个非常模糊的领域。 在C++的标准化过程中, 关于这个问题有很多讨论 你是否能投一个 来自一个类的成员函数指针 指向基的成员函数指针 或派生类,以及 可以在不相关的类之间转换。 当标准委员会 下定决心,不同的编译器 卖主们已经做出了决定 已作出的执行决定 将他们锁定在不同的答案中 这些问题。[]


    假设我们有
    类X、类Y:public X和类Z:public Y

    您应该能够为x、y两种方法分配空类型(y::*p)()的指针,而不是Z的方法,看看为什么要考虑以下内容:

    void (Y::*p)() = &Z::func; // we pretend this is legal
    Y * y = new Y; // clearly legal
    (y->*p)(); // okay, follows the rules, but what would this mean?
    
    通过允许该赋值,我们允许在Y对象上调用Z的方法,这可能导致谁知道发生了什么。您可以通过强制转换指针使其全部工作,但这既不安全,也不能保证工作。

    C++03 std,:

    “指向cv T类型B的成员的指针”类型的右值,其中B是类类型,可以转换为“指向cv T类型D的成员的指针”类型的右值,其中D是B的派生类(第10条)。如果B是不可访问的(第11条)、不明确的(10.2)或虚拟的(10.1)D的基类,需要进行此转换的程序格式不正确。转换的结果引用与转换前指向成员的指针相同的成员,但它引用基类成员,就像它是派生类的成员一样。结果引用D的实例B中的成员。因为结果h作为类型“指向cv T类型的D成员的指针”,它可以与D对象解除引用。结果与指向B成员的指针与D的B子对象解除引用的结果相同。空成员指针值转换为目标类型的空成员指针值。52)

    52)指针到成员的转换规则(从指向基成员的指针到指向派生成员的指针)与指向对象的指针的转换规则(从指向派生成员的指针到指向基的指针)相反(4.10,第10条)。此反转是确保类型安全所必需的。请注意,指向成员的指针不是指向对象的指针或指向函数的指针,此类指针的转换规则不适用于指向成员的指针。特别是,指向成员的指针不能转换为空*

    简而言之,您可以将指向可访问的非虚拟基类成员的指针转换为指向派生类成员的指针,只要该成员不具有歧义性

    class A {
    public: 
        void foo();
    };
    class B : public A {};
    class C {
    public:
        void bar();
    };
    class D {
    public:
        void baz();
    };
    class E : public A, public B, private C, public virtual D {
    public: 
        typedef void (E::*member)();
    };
    class F:public E {
    public:
        void bam();
    };
    ...
    int main() {
       E::member mbr;
       mbr = &A::foo; // invalid: ambiguous; E's A or B's A?
       mbr = &C::bar; // invalid: C is private 
       mbr = &D::baz; // invalid: D is virtual
       mbr = &F::bam; // invalid: conversion isn't defined by the standard
       ...
    
    在另一个方向上的转换(通过
    静态\u cast
    )由9控制:

    如果存在从“指向类型为T的B的成员的指针”到“指向类型为T的D的成员的指针”的有效标准转换,则类型为“指向类型为cv2 T的B的成员的指针”的右值可以转换为类型为“指向类型为T的D的成员的指针”的右值,其中B是D的基类(子句),且cv2的cv限定与空成员指针值()相同,或大于cv1.11)转换为目标类型的null成员指针值。如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,转换的结果未定义。[注意:尽管B类不需要包含原始成员,但对其取消引用成员指针的对象的动态类型必须包含原始成员;请参阅。]

    11) 函数类型(包括指向成员函数的指针中使用的类型 类型)不符合cv条件;请参阅


    简而言之,您可以从der转换
    void (Y::*p)() = &Y::y_fn;
    void (Z::*q)() = p; // legal and safe
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
        virtual void traverse(string arg) {
            find(&A::visit, arg);
        }
    
    protected:
        virtual void find(void (A::*method)(string arg),  string arg) {
            (this->*method)(arg);
        }
    
        virtual void visit(string arg) {
            cout << "A::visit, arg:" << arg << endl;
        }
    };
    
    class B : public A {
    protected:
        virtual void visit(string arg) {
            cout << "B::visit, arg:" << arg << endl;
        }
    };
    
    int main()
    {
        A a;
        B b;
        a.traverse("one");
        b.traverse("two");
        return 0;
    }