简体   繁体   English

访问父类C ++中的子成员

[英]Access child members within parent class, C++

I am facing a situation where I need to access child member variables inside the parent class. 我面临的情况是我需要在父类中访问子成员变量。 I know this is against OO principles but I have to deal with a scenario where hundreds of classes are inheriting from one and along the way half of them stopped using one of the parents variable, and declared and used their own (there was need to switch from int to int[] and apparently the person who did that didn't take in consideration to apply this changes in the parent class instead). 我知道这是违反OO原则的,但是我必须处理一个场景,其中有数百个类继承自一个类,并且其中一半停止使用其中一个父变量,并声明并使用它们自己(需要切换)从int到int [],显然那个人没有考虑在父类中应用这些更改。

One option is to have a virtual function to deal with it, but this means I have to change the code in hundreds of file/objects and test each one of them. 一种选择是使用虚拟函数来处理它,但这意味着我必须更改数百个文件/对象中的代码并测试它们中的每一个。 Hence I thought if it is possible to use some old school C pointer magic to gain access to this variables inside the parent method, this will eliminate the need of hundreds of virtual functions. 因此,我认为如果可以使用一些旧的学校C指针魔术来获取父方法内部的这些变量,这将消除数百个虚函数的需要。

Basically this is what I want to achieve: 基本上这就是我想要实现的目标:

class Parent
{
    void DoSomething()
    {
        // This is what I need
        childMember = 0;
    }
}

class Child1 : Parent
{
    int childMember;
}

class Child2 : Parent
{
    int childMember;
}

Please let me know if this is even possible. 如果可能,请告诉我。 If yes how do I achieve that. 如果是,我该如何实现。
Other suggestions are welcomed, but keep in mind that I'd like to make changes only in the parent class. 欢迎其他建议,但请记住,我只想在父类中进行更改。
TIA. TIA。

The only clean way is to use the virtual function approach. 唯一干净的方法是使用虚函数方法。

If the Parent class has at least one virtual function (not necessarily DoSomething ), there's also a yucky way to do it: 如果Parent类至少有一个虚函数(不一定是DoSomething ),那么还有一种令人讨厌的方法:

void DoSomething() {
    if (Child1* child = dynamic_cast<Child1*>(this)) {
        child->childMember = 0;
    } else if (Child2* child = dynamic_cast<Child2*>(this)) {
        child->childMember = 0;
    } // and so on, and so forth
}

(If Parent has no virtual functions, then the dynamic_cast won't work. Though, any class designed to be inherited from should have at least one virtual function, even if it's just the destructor.) (如果Parent没有虚函数,那么dynamic_cast将不起作用。但是,任何设计为继承的类都应该至少有一个虚函数,即使它只是析构函数。)

Probably CRTP helps here: CRTP可能对此有所帮助:

struct Parent
{
    virtual void DoSomething() = 0;
};

template <typename Derived>
struct ParentProxy : Parent
{
    virtual void DoSomething()
    {
        Derived* p = dynamic_cast<Derived*>(this);
        p->childMember = 27;
    }
};

struct Child1 : ParentProxy<Child1>
{
    int childMember;
};

struct Child2 : ParentProxy<Child2>
{
    int childMember;
};

int main()
{
    Child1 child1;
    Child2 child2;

    Parent* objects[] = { &child1, &child2 };
    const int objectCount = sizeof(objects) / sizeof(objects[0]);
    for (int index = 0; index < objectCount; ++index)
    {
        Parent* parent = objects[index];
        parent->DoSomething();
    }
}

I changed the code that it is compilable - probably not what you're requirements are - but then provide a better (=compilable) sample code. 我改变了可编译的代码 - 可能不是你的要求 - 然后提供更好的(=可编译的)示例代码。

If you're allowed to change your child classes source code, you can do something like that: 如果您被允许更改子类源代码,您可以执行以下操作:

class Parent
{
public:
    void DoSomething()
    {
        getMember() = 0;
    }
    virtual int & getMember() = 0;
};

class Child1 : public Parent
{
    int childMember;
public:
    int & getMember()
    {
        return childMember;
    }
};

class Child2 : public Parent
{
    int childMember;
public:
    int & getMember()
    {
        return childMember;
    }
};

Otherwise, if your object has virtual table (at least one virtual method), you can use static_cast() in combination with C++11 typeid, because it's about three times faster than dynamic_cast: 否则,如果您的对象具有虚拟表(至少一个虚方法),则可以将static_cast()与C ++ 11 typeid结合使用,因为它比dynamic_cast快三倍:

#include <typeinfo>

class Parent
{
public:
    virtual void DoSomething();
};

class Child1 : public Parent
{
public:
    int childMember;
};

class Child2 : public Parent
{
public:
    int childMember;
};

void Parent::DoSomething()
{
    if (typeid(Child1) == typeid(*this))
    {
        auto child = static_cast<Child1*>(this);
        child->childMember = 0;
    }
    else if (typeid(Child2) == typeid(*this))
    {
        auto child = static_cast<Child2*>(this);
        child->childMember = 0;
    }
};

I dont get the downvotes for the static cast. 我不会得到静态演员的downvotes。 The following works: 以下作品:

#include <stdio.h>
class B;
class A {
    public:
        A();
        void print();
    private:
        B *child;
};

class B : public A {
    friend class A;
    public:
        B();
    private:
        int value;
};

A::A(){
    child = static_cast<B*>(this);
}

void A::print(){
    printf("value = %d\n", child->value);
}

B::B(){
    value = 10;
}

int main(){
    B b;
    b.A::print();
}

Just make sure to put the declarations of the A functions after the definition of the B class. 只需确保在B类的定义之后放置A函数的声明。 You can differentiate between child classes because the static cast will return not NULL if you have found the right child. 您可以区分子类,因为如果找到了正确的子项,静态强制转换将不返回NULL。 I find this approach also beneficial because the Child class (B) is barely changed. 我发现这种方法也很有用,因为Child类(B)几乎没有改变。

Apparently you have some subset of derived classes which utilise childMember . 显然,您有一些使用childMember的派生类子集。 For these classes you have some method "DoSomething()" which can be called. 对于这些类,您可以使用一些可以调用的方法“DoSomething()”。 I guess for all other derived classes the method DoSomething() is not applicable . 我想对于所有其他派生类,方法DoSomething() 不适用 Why don't you create another abstraction level for this set of derived classes? 为什么不为这组派生类创建另一个抽象级别?

class Parent
{
    // no DoSomething()
}

class ChildMemberClasses : Parent
{
    int childMember;

    void DoSomething()
    {
        // code that uses childMember
    }
}

class ChildWithChildMember : ChildMemberClasses
{
    // other stuff
}

If DoSomething() has some meaning for classes without childMember, you can still define the it as virtual method in Parent . 如果DoSomething()对没有childMember的类有一定的意义,你仍然可以在Parent中将它定义为虚方法。 Like this: 像这样:

class Parent
{
    virtual void DoSomething()
    {
        // code that does not use childMember
    }
}

class ChildMemberClasses : Parent
{
    int childMember;

    void DoSomething()
    {
        // code that uses childMember
    }
}

class ChildWithChildMember : ChildMemberClasses
{
    // other stuff
}

You can use the curiously recurring template pattern to achieve this. 您可以使用奇怪的重复模板模式来实现此目的。

template<typename T>
class Parent
{
    void DoSomething()
    {
        // This is what I need
        T::childMember = 0;
    }

    virtual ~Parent() {}
};

class Child1 : Parent<Child1>
{
  int childMember;

  friend class Parent<Child1>;
};

Would making an intermediate abstract class that contains the childMember you need to access be an option? 是否需要创建一个包含您需要访问的childMember的中间抽象类?

If so: 如果是这样:

class Parent
{
    virtual void DoSomething() //Parent must be a polymorphing type to allow dyn_casting
    {
        if (AbstractChild* child = dynamic_cast<AbstractChild*>(this)) {
            child->childMember = 0;
        } else //Its not an AbstractChild
    }
}

class AbstractChild : Parent 
{ 

     int childMember; 
     virtual void DoSomething() = 0;
}

class Child1 : AbstractChild {
     virtual void DoSomething() { }
}

There is no safe, robust method of doing this with pointers. 使用指针没有安全,可靠的方法。 You could hack around with clever pointer offsets, but this would rely on the childMember appearing in the same location in all of the child classes. 您可以使用聪明的指针偏移来解决问题,但这将依赖于出现在所有子类中相同位置的childMember

static_cast<Child*>(this)->childMember = 0; should work. 应该管用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM