Copying Methods from Member

2019-04-08 18:43发布

I have a simple, low-level container class that is used by a more high-level file class. Basically, the file class uses the container to store modifications locally before saving a final version to an actual file. Some of the methods, therefore, carry directly over from the container class to the file class. (For example, Resize().)

I've just been defining the methods in the file class to call their container class variants. For example:

void FileClass::Foo()
{
    ContainerMember.Foo();
}

This is, however, growing to be a nuisance. Is there a better way to do this?

Here's a simplified example:

class MyContainer
{
    // ...

    public:

    void Foo()
    {
        // This function directly handles the object's
        // member variables.
    }
}

class MyClass
{
    MyContainer Member;

    public:

    void Foo()
    {
        Member.Foo();

        // This seems to be pointless re-implementation, and it's
        // inconvenient to keep MyContainer's methods and MyClass's
        // wrappers for those methods synchronized.
    }
}

5条回答
迷人小祖宗
2楼-- · 2019-04-08 19:21

Yes, maintaining a proxy class like this is very annoying. Your IDE might have some tools to make it a little easier. Or you might be able to download an IDE add-on.

But it isn't usually very difficult unless you need to support dozens of functions and overrides and templates.

I usually write them like:

void Foo()      { return Member.Foo(); }
int  Bar(int x) { return Member.Bar(x); }

It's nice and symmetrical. C++ lets you return void values in void functions because that makes templates work better. But you can use the same thing to make other code prettier.

查看更多
3楼-- · 2019-04-08 19:22

Well, why not just inherit privatly from MyContainer and expose those functions that you want to just forward with a using declaration? That is called "Implementing MyClass in terms of MyContainer.

class MyContainer
{
public:
    void Foo()
    {
        // This function directly handles the object's
        // member variables.
    }

    void Bar(){
      // ...
    }
}

class MyClass : private MyContainer
{
public:
    using MyContainer::Foo;

    // would hide MyContainer::Bar
    void Bar(){
      // ...
      MyContainer::Bar();
      // ...
    }
}

Now the "outside" will be able to directly call Foo, while Bar is only accessible inside of MyClass. If you now make a function with the same name, it hides the base function and you can wrap base functions like that. Of course, you now need to fully qualify the call to the base function, or you'll go into an endless recursion.


Additionally, if you want to allow (non-polymorphical) subclassing of MyClass, than this is one of the rare places, were protected inheritence is actually useful:

class MyClass : protected MyContainer{
  // all stays the same, subclasses are also allowed to call the MyContainer functions
};

Non-polymorphical if your MyClass has no virtual destructor.

查看更多
混吃等死
4楼-- · 2019-04-08 19:25

Consider what makes sense in your case - composition (has a) or inheritance (is a) relationship between MyClass and MyContainer.

If you don't want to have code like this anymore, you are pretty much restricted to implementation inheritance (MyContainer as a base/abstract base class). However you have to make sure this actually makes sense in your application, and you are not inheriting purely for the implementation (inheritance for implementation is bad).

If in doubt, what you have is probably fine.

EDIT: I'm more used to thinking in Java/C# and overlooked the fact that C++ has the greater inheritance flexibility Xeo utilizes in his answer. That just feels like nice solution in this case.

查看更多
Summer. ? 凉城
5楼-- · 2019-04-08 19:36

That's delegation inheritance and I don't know that C++ offers any mechanism to help with that.

查看更多
贪生不怕死
6楼-- · 2019-04-08 19:37

This feature that you need to write large amounts of code is actually necessary feature. C++ is verbose language, and if you try to avoid writing code with c++, your design will never be very good.

But the real problem with this question is that the class has no behaviour. It's just a wrapper which does nothing. Every class needs to do something other than just pass data around.

The key thing is that every class has correct interface. This requirement makes it necessary to write forwarding functions. The main purpose of each member function is to distribute the work required to all data members. If you only have one data member, and you've not decided yet what the class is supposed to do, then all you have is forwarding functions. Once you add more member objects and decide what the class is supposed to do, then your forwarding functions will change to something more reasonable.

One thing which will help with this is to keep your classes small. If the interface is small, each proxy class will only have small interface and the interface will not change very often.

查看更多
登录 后发表回答