Search code examples
c++forward-declaration

How to solve this forward class declaration problem?


I am having a following situation:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Forward declarations
class Owner;
class Item;

class Owner {
public:
    string name;
    vector<Item> item;
    Owner(string _name, initializer_list<Item> _item) : name(_name), item(_item) {
        for (size_t i = 0; i < item.size(); i++)
            item[i].owner = this;
    }
    // change the owner when copying...
    Owner(const Owner& o) {
        name = o.name;
        item = o.item;
        for (size_t i = 0; i < item.size(); i++)
            item[i].owner = this; // is this OK? 
    }
    Owner& operator = (const Owner& o) {
        name = o.name;
        item = o.item;
        for (size_t i = 0; i < item.size(); i++)
            item[i].owner = this; 
        return *this;
    }
};
class Item {
public:
    string name;
    Owner* owner;
    Item(string _name) : name(_name) {}
    string getInfo() {
        return name + " owned by " + owner->name;
    }
};
int main(){
    Item Cup("Cup"), Plate("Plate");
    Owner John("John", { Cup, Plate });
    cout << Cup.getInfo() << endl; // expecting "Cup owned by John"
    return 0;
}

One owner can have many items, but each item also must contain info about its owner. When I run this I get

Error C2027 use of undefined type 'Item'

I thought this would be solved by using forward class declarations, but no such luck. Also, I'm guessing my copy constructor and assignment operators are OK?


Solution

  • Forward declaring the type is only part of the solution. You also need move defintions of functions of at least one of the types when dependencies are circular - usually put in .cpp file (but not neccesary as seen in example).

    Working example:

    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    // Forward declarations
    class Owner;
    
    class Item {
    public:
        string name;
        Owner* owner;
        Item(string _name) : name(_name) {}
        string getInfo();//<-- only declaration now
    };
    
    class Owner {
    public:
        string name;
        vector<Item> item;
        Owner(string _name, initializer_list<Item> _item) : name(_name), item(_item) {
            for (size_t i = 0; i < item.size(); i++)
                item[i].owner = this;
        }
        // change the owner when copying...
        Owner(const Owner& o) {
            name = o.name;
            item = o.item;
            for (size_t i = 0; i < item.size(); i++)
                item[i].owner = this; // is this OK? 
        }
        Owner& operator = (const Owner& o) {
            name = o.name;
            item = o.item;
            for (size_t i = 0; i < item.size(); i++)
                item[i].owner = this; 
            return *this;
        }
    };
    
    //or put in .cpp file
        string Item::getInfo() {
            return name + " owned by " + owner->name;
        }
    
    int main(){
        Item Cup("Cup"), Plate("Plate");
        Owner John("John", { Cup, Plate });
        cout << Cup.getInfo() << endl; // expecting "Cup owned by John"
        return 0;
    }
    

    Try it yourself

    Furthermore, you copy the Items into the owner, so when you print the original, it only prints "Cup owned by " and "John", the owner, is missing. It's not clear which way this should be fixed - but could eg. be fixed by storing std::shared_ptr instead if the items should be shared.