0

This is a question to find out the better programming practice:

In C++, say I have two classes one of which is a member class of the other, e.g.,

class SomeClass {

 public:

  MemberClass member_class;

  void set_num(double num_) { num_ = num; }

  double num() {return num_; }

 private:

  double num_;

}

I want the member class to have access to the member functions of the outer class, e.g.,

class MemberClass {

 public:

  PrintSquare() {

    cout << num() * num() << endl;

  }

}

I am trying to achieve this in order to reduce the number of function arguments I am passing all around the program.

devotee
  • 127
  • 11
  • There is no "member class" here, only lots of member *objects*. The types of all the member objects appear to be entirely unrelated to the containing class. – Kerrek SB Jun 25 '14 at 20:27
  • `num()` is a function of `SomeClass`, not `MemberClass`. Your example `PrintSquare()` makes no sense. – Red Alert Jun 25 '14 at 20:28
  • You may need to provide a more concrete example. It _looks_ like `MemberClass` and `SomeClass` should just be the same one class, but it's hard to offer better design when your example is abstract. Why are there two classes? – Drew Dormann Jun 25 '14 at 20:29
  • 1
    Moreover, since you're going to have to call `myobj.member_class.PrintSquare()` anyway, why not just make `PrintSquare()` a member of `SomeClass`? – Crowman Jun 25 '14 at 20:29
  • You are right that they seem to be better combined together. But, indeed, they are two huge classes. So, I am not willing to combine the two classes. – devotee Jun 25 '14 at 20:32
  • 2
    If they are two huge classes, they should probably both be broken up into some number of much smaller classes. Decouple as much as possible. You will almost certainly find that what you need to do becomes much easier. – Rob K Jun 25 '14 at 20:39
  • @RobK _... broken up into some number of much smaller classes. ..._ Or even better _interfaces_. – πάντα ῥεῖ Jun 25 '14 at 23:57

3 Answers3

2

The most common (and IMHO proper) way to solve this problem is, introducing an interface (or even more interfaces focusing on particular sets of method features) for the containing class, and pass that one to the 'inner' class member on construction:

struct Interface {
  virtual void set_num(double num_) = 0;
  virtual double num() const = 0;
  virtual ~Interface() {}
};

class MemberClass {
public:
    MemberClass(Interface& interface) : interface_(interface) {}
    PrintSquare() {
        cout << interface_.num() * interface_.num() << endl;
    }

private:
    Interface& interface_;
};

class SomeClass : public Interface {
public:
    MemberClass member_class;

    SomeClass() : member_class(*this), num_() {}

    virtual void set_num(double num_) { num_ = num; }
    virtual double num() const { return num_; }
    virtual SomeClass() {}

private:
    double num_;
};

NOTE: Calling methods of the interface though will fail (with a runtime exception), when called from the MemberClass constructor definition.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
1

Although the answer by Kerrek is very interesting, he himself already states this normally isn't the way to go. Common practice would be to make the inner class nested in the outer one, if possible. If the inner one needs access to the outer one in such a way that a nested connection seems natural, this would be the way to go. Construction of an Inner object would then need a reference to the object it is a member from, in order to be able to call functions on its parent:

class Outer
{
    class Inner
    {
        Outer &parent; // consider constness
    public:
        Inner(Outer &_parent); //initializes the parent-reference
        void innerFunction(); // can call members of parent    
    };

    Inner inner;
public: 
    Outer(): inner(*this) { ... } // initialize inner
};

Depending on the standard you're using, the innerFunction now has access to all public members of Outer (C++03), or even all private members as well (C++11). See also this topic: C++ nested classes - inner/outer relationship

EDIT: Did a quick test, and my compiler (gcc 4.7.2) also allows access to private members with older standards. Maybe someone could comment on this...

Community
  • 1
  • 1
JorenHeit
  • 3,877
  • 2
  • 22
  • 28
  • Hmm :-(, I don't like the kind of tight coupling classes your proposal introduces. – πάντα ῥεῖ Jun 26 '14 at 01:30
  • Thats why I said to use this if it is natural to the problem. I can't know if this is the case, which is why I added it as a restriction for use. My experience tells me that when a member needs to call members of its parent, nesting often is the natural connection. Otherwise, circular dependencies arise and one has to think of more complicated solutions like yours. It's up to the problem details to see which is more appropriate. – JorenHeit Jun 26 '14 at 05:31
  • @πάνταῥεῖ Just wanted to add that in the problem, the two classes *are* tightly coupled, so a solution in which they are seems at least worth considering. (Sorry, couldn't direct the previous comment directly to you on my phone.) – JorenHeit Jun 26 '14 at 07:18
0

If your classes are all standard-layout, then you can take advantage of some layout guarantees that C++ makes, namely that a on object of standard layout type may be treated as if it were its own first member. For instance:

struct Foo
{
    int a;

    void barely_legal();
};

struct Bar
{
    Foo x;
    int y;
};

#include <type_traits>

void Foo::barely_legal()
{
    static_assert(std::is_standard_layout<Foo>::value, "Foo");
    static_assert(std::is_standard_layout<Bar>::value, "Bar");

    Bar * p = reinterpret_cast<Bar *>(this);

    ++p->y;
}

This is unusual at best and cruel at worst, so please don't write code like this unless you have a really good reason to do so. (I know people who do have reason to do this, but I don't turn my back towards them.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I'm tempted to downvote this answer, because it contains this surely not to be advised code. But at least you stated this yourself ... – πάντα ῥεῖ Jun 25 '14 at 20:35
  • 2
    @πάνταῥεῖ: Hey, sometimes people have hard problems and it's useful to know all the options, even if they're bad ideas. Like democracy ;-) (Also, just like in democracy, you're invited to participate and post your own answer, and I'll upvote it.) – Kerrek SB Jun 25 '14 at 20:37
  • The more general approach would be to use [offsetof](http://en.cppreference.com/w/cpp/types/offsetof). – D Drmmr Jun 25 '14 at 21:41
  • @DDrmmr: That's right. The approach can readily be generalized to add an offset (though you have to go through an intermediate `char *`). – Kerrek SB Jun 25 '14 at 21:48
  • @KerrekSB I've found time enough now to give an answer. Your critiques about democracy are arguable ;) ... – πάντα ῥεῖ Jun 25 '14 at 23:24