C++ Nested classes driving me crazy

17,498

Solution 1

Add a couple of constructors and a pointer to the parent class.

#include <string>
#include <vector>
class myList
{
public:
    std::vector<std::string> vec;
    myList(): items(this) {} // Added
    class Items
    {
    public:
        Items(myList *ml): self(ml) {}  // Added
        void Add(std::string str)
        {
                self->vec.push_back(str); // Changed
        };
        myList *self; //Added
    }items;
};

int main()
{
    myList newList;
    newList.items.Add("A");
}

You need the myList() constructor, so it registers instances of itself with the instance of the inner class member variable. Then you need the Items constructor to store the pointer to the outer myList class instance. Finally in the Add method, you need to reference vec in the stored myList instance.

As Catskul points out, the Item constructor mustn't actually do anything with the myList pointer it receives. I'd also like to say that though this answer is closer to the original intent, steveth45's answer is closer to what you would want to do in a real program.

Solution 2

This way you aren't exposing your class members directly. Your example seems over-architected a bit. Why put a std::vector into a class and then expose it as public?

class myList
{
private:
    std::vector<std::string> vec;
public:
    void Add(std::string str)
    {
        vec.push_back(str);
    };
};

int main()
{
    myList newList;
    newList.Add("A");
}

Solution 3

Unlike Java, inner objects in C++ don't have access to an outer 'this' pointer ... if you think about it there may be cases where there isn't one to reference.

Richard Quirk's solution is the nearest you can get in C++

Solution 4

Inner classes are only related by name. You can't refer to the vector in the base class like that.

You either need to move the vector in to the inner class or store a reference to it.

Solution 5

While this post is a few years old I might be able to add something useful to it. While I will say that the design of the class in the original post doesn't look that great, there are times where it's useful to have an embedded class be able to access the containing class. This can easily be done without storing extra pointers. Below is an example. It should work as I took it from some existing code and changed some names around. The key is the EmbeddorOf macro. Works like a charm.

//////////////////// .h file /////////////////////////

struct IReferenceCounted
{
    virtual unsigned long AddRef() = 0;
    virtual unsigned long Release() = 0;
};

struct IFoo : public IReferenceCounted
{
};

class Foo : public IFoo
{
public:
    static IFoo* Create();
    static IFoo* Create(IReferenceCounted* outer, IReferenceCounted** inner);

private:
    Foo();
    Foo(IReferenceCounted* outer);
    ~Foo();

    // IReferenceCounted

    unsigned long AddRef();
    unsigned long Release();

private:
    struct EIReferenceCounted : IReferenceCounted
    {
        // IReferenceCounted

        unsigned long AddRef();
        unsigned long Release();
    } _inner;

    unsigned long _refs;
    IReferenceCounted* _outer;
};

//////////////// .cpp file /////////////////

#include <stdio.h>
#include <stddef.h>
#include "Foo.h"

#define EmbeddorOf(class, member, this) \
    (class *) ((char *) this - offsetof(class, member))

// Foo

Foo::Foo() : _refs(1), _outer(&this->_inner)
{
}

Foo::Foo(IReferenceCounted* outer) : _refs(1), _outer(outer)
{
}

Foo::~Foo()
{
    printf("Foo::~Foo()\n");
}

IFoo* Foo::Create()
{
    return new Foo();
}

IFoo* Foo::Create(IReferenceCounted* outer, IReferenceCounted** inner)
{
    Foo* foo = new Foo(outer);
    *inner = &foo->_inner;
    return (IFoo*) foo;
}

// IReferenceCounted

unsigned long Foo::AddRef()
{
    printf("Foo::AddRef()\n");
    return this->_outer->AddRef();
}

unsigned long Foo::Release()
{
    printf("Foo::Release()\n");
    return this->_outer->Release();
}

// Inner IReferenceCounted

unsigned long Foo::EIReferenceCounted::AddRef()
{
    Foo* pThis = EmbeddorOf(Foo, _inner, this);
    return ++pThis->_refs;
}

unsigned long Foo::EIReferenceCounted::Release()
{
    Foo* pThis = EmbeddorOf(Foo, _inner, this);
    unsigned long refs = --pThis->_refs;
    if (refs == 0)
        {

        // Artifically increment so that we won't try to destroy multiple
        // times in the event that our destructor causes AddRef()'s or
        // Releases().

        pThis->_refs = 1;
        delete pThis;
        }
    return refs;
}

Nick

Share:
17,498
salvalcantara
Author by

salvalcantara

Software Analyst

Updated on June 04, 2022

Comments

  • salvalcantara
    salvalcantara almost 2 years

    i am trying to compile this very simple piece of code

    class myList
    {
    public:
        std::vector<std::string> vec;
        class Items
        {
        public:
            void Add(std::string str)
            {
                myList::vec.push_back(str);
            };
        }items;
    };
    
    int main()
    {
        myList newList;
        newList.items.Add("A");
    }
    

    what can i do to make this work without creating more objects that needed or overcomplicating stuff...

  • salvalcantara
    salvalcantara over 15 years
    thanks very much, this works, but i was hoping that a simpler version existed, but i must continue to cope with C++ stuborness :-( hope C++0x solves some of those quirks
  • Catskul
    Catskul over 14 years
    ... but be careful with passing the MyList this pointer while MyList is still being constructed. The Items constructor can get itself in trouble if it does anything that ends up using the MyList this pointer too early.
  • Sharon
    Sharon about 14 years
    The original author probably needs the inner class construct for other stuff he's not mentioning (for simplicity of his exposition). You're right about the software engineering soft aspect given existing information but the problem seems like a "toy" abstracting compilation problems for could originally have been a quite complex algorithm. It's not important really, you probably understand already, I'm just saying...