http://s11.postimg.org/kxl3cemw3/error.png
this is the least error that I can get from multiple time of debugging.

TheCodeFile

StrBlob.h has needed to use something from StrBlobPtr.h
and
StrBlobPtr.h needed to use something from StrBlob.h

I did tried to #include each file on each other respectively, but ofcourse that would be a data structure error(that can't be fixed) due to infinite recurssion.
so what should I do to fix this?

P.S. the code is purely come from a c++ tutorial book "C++ Primer 5th edition". Yet, the exercise did tell to add there appropriate friend declaration, but I did all of them, the forward declaration, include stuff and the friend thingy but nothing works, nothing!

Recommended Answers

All 6 Replies

I am not 100% sure what you are asking since I can't access the code. However I think I understand what you are asking. You have two classes that refer to each other and want to have each in their own headers. The key to remember is that the #include "fileName.h" line acts identically to a copy-paste of the contents of fileName.h into your file, turning your entire program into one big file (see http://en.cppreference.com/w/cpp/language/translation_phases).

To have two classes refer to each other in one file this is how you do it:

#include <iostream>
class A; //forward declaration of class A
class B; //forward declaration of class B
class A
{
    B *b;
    public:
    A():b(NULL){}
    A(B *b):b(b){}
    A &set(B *b){this->b=b;return *this;}
    A &print(){std::cout<<"A"<<std::endl;return *this;}
    B *get(){return b;}
};
class B
{
    A *a;
    public:
    B():a(NULL){}
    B(A *a):a(a){}
    B &set(A *a){this->a=a;return *this;}
    B &print(){std::cout<<"B"<<std::endl;return *this;}
    A *get(){return a;}
};
int main()
{
    A a;
    B b(&a);
    a.set(&b);
    a.print();
    b.print();
    a.get()->print();
    b.get()->print();
    return 0;
}

Which works as expected, printing ABBA across four lines. Now when we make our header files we want to ensure that including them generates the same code. As such we link the class files with a header file that contains the forward declarations, like so:

AB.h:

#ifndef AB_H
#define AB_H

#include <iostream>

class A;//forward declaration
class B;//forward declaration

#include "A.h"
#include "B.h"

#endif //AB_H

A.h:

#ifndef A_H
#define A_H

class A
{
    B *b;
    public:
    A():b(NULL){}
    A(B *b):b(b){}
    A &set(B *b){this->b=b;return *this;}
    A &print(){std::cout<<"A"<<std::endl;return *this;}
    B *get(){return b;}
};


#endif //A_H

B.h:

#ifndef B_H
#define B_H

class B
{
    A *a;
    public:
    B():a(NULL){}
    B(A *a):a(a){}
    B &set(A *a){this->a=a;return *this;}
    B &print(){std::cout<<"B"<<std::endl;return *this;}
    A *get(){return a;}
};

#endif //B_H

test.cpp:

#include "myClasses.h" //comes with iostream :P

int main()
{
    A a;
    B b(&a);
    a.set(&b);
    a.print();
    b.print();
    a.get()->print();
    b.get()->print();
    return 0;
}

Running test.cpp generates the expected output of ABBA as well because if you go through the #includes and do the copy-paste you see that the resulting code is actually the same.

However you have another issue in that you are getting "incomplete type" errors. This is because after you have forward declared a class you still can't create objects of it because class A; A x; makes no sense, the compiler has no idea how to make an A. Because of this, until you have defined your class, you cannot create real objects with it. Instead you have to use pointers, references, or wrappers (which will use pointers or references internally). That is why the A and B classes above refer to each other as pointers rather than real types.

Hope this helped.

As shown by Labdabeta, make sure you "guard" your header files from multiple inclusions.

Meanwhile I working on it, here the CodeFile

(with a bit of update based on your reply, still in error, of course)

btw, what about the source file?
should I add anything to them?

Additional information:
using MingW32 console Compiler & notepad++

bump

Here is the corrected code:

StrBlob.h:

#ifndef EXERCISE_STR_BLOB_H_
#define EXERCISE_STR_BLOB_H_

#include <vector>
#include <string>
#include <memory>

class StrBlobPtr;

class StrBlob {
  friend class StrBlobPtr;
public:
  typedef std::vector<std::string>::size_type size_type;
  StrBlob();
  StrBlob(std::initializer_list<std::string> il);
  std::size_t size() const { return data->size(); }
  bool empty() const { return data->empty(); }

  // add and remove elements
  void push_back(const std::string &t)
    { data->push_back(t); }
  void pop_back();

  // element access
  std::string& front();
  std::string& back();


  StrBlobPtr begin();
  StrBlobPtr end();

private:
  std::shared_ptr<std::vector<std::string> > data;
  // throw msg if data[i] isn't valid
  void check(std::size_t i, const std::string &msg) const;
};

#endif

StrBlobPtr.h:

#ifndef EXERCISE_STR_BLOB_PTR_H_
#define EXERCISE_STR_BLOB_PTR_H_

#include <vector>
#include <string>
#include <memory>

class StrBlob; // forward declaration

class StrBlobPtr {
public:
  StrBlobPtr(): curr(0) { }
  StrBlobPtr(StrBlob &a, std::size_t sz = 0);
  std::string& deref() const;
  StrBlobPtr& incr();  // prefix version

private:
  std::shared_ptr<std::vector<std::string> >
    check(std::size_t, const std::string&) const;
  std::weak_ptr<std::vector<std::string> > wptr;
  std::size_t curr;
};

#endif

StrBlob.cpp:

#include "StrBlob.h"
#include "StrBlobPtr.h"

StrBlob::StrBlob() : data(std::make_shared<std::vector<std::string> >()) { }

StrBlob::StrBlob(std::initializer_list<std::string> il) :
  data(std::make_shared<std::vector<std::string> >(il)) { }

void StrBlob::check(std::size_t i, const std::string &msg) const
{
  if (i >= data->size())
    throw std::out_of_range(msg);
}

std::string& StrBlob::front()
{
  // if the vector is empty, check will throw
  check(0, "front on empty StrBlob");
  return data->front();
}
std::string& StrBlob::back()
{
  check(0, "back on empty StrBlob");
  return data->back();
}
void StrBlob::pop_back()
{
  check(0, "pop_back on empty StrBlob");
  data->pop_back();
}

StrBlobPtr StrBlob::begin() { 
  return StrBlobPtr(*this); 
}

StrBlobPtr StrBlob::end() { 
  auto ret = StrBlobPtr(*this, data->size());
  return ret; 
}

StrBlobPtr.cpp:

#include "StrBlobPtr.h"
#include "StrBlob.h"

StrBlobPtr::StrBlobPtr(StrBlob &a, std::size_t sz) :
    wptr(a.data), curr(sz) { }

std::shared_ptr<std::vector<std::string> > 
  StrBlobPtr::check(std::size_t i, const std::string &msg) const
{
  auto ret = wptr.lock();
  if (!ret)
    throw std::runtime_error("unbound StrBlobPtr");
  if (i >= ret->size())
    throw std::out_of_range(msg);
  return ret;
}

std::string& StrBlobPtr::deref() const
{
  auto p = check(curr, "dereference past end");
  return (*p)[curr]; // (*p) is the vector to which this object point.
}

StrBlobPtr& StrBlobPtr::incr()
{
  check(curr, "increment past end of StrBlobPtr");
  ++curr;
  return *this;
}

main.cpp:

#include "StrBlob.h"
#include "StrBlobPtr.h"

#include <iostream>

int main()
{
  StrBlob b1;
  {
    StrBlob b2 = {"a", "an", "the"};
    std::cout << b1.size() << std::endl
              << b2.size() << std::endl;

    std::cout << b2.back() << std::endl;

    b1 = b2;
    std::cout << b1.size() << std::endl
              << b2.size() << std::endl;

    std::cout << b1.back() << std::endl;
    std::cout << b2.back() << std::endl;

    b2.push_back("about");
    std::cout << b1.size() << std::endl
              << b2.size() << std::endl;
  }

  std::cout << b1.size() << std::endl;

  return 0;
}

Here are the things that I modified and that you should notice as being the key elements:

1) To resolve the "incomplete type" problems, you need to make sure that you never "use" an incomplete type. A forward-declared class is an incomplete type because the compiler only knows that the class exists, but it has no idea what that class looks like (what it contains, what functions it has, etc.). Because the compiler has no idea what the class is, it cannot compile any uses of it, that includes creating an object of that class, calling a member function, or accessing a data member. So, when you use a forward-declaration of StrBlob in order to declare the constructor of StrBlobPtr, it is fine because the parameter is a reference, not an object. A reference to an incomplete type is fine because the compiler does not need to know what the type looks like in order to just form a reference to an object of that type. However, in the body of that constructor, you access the data data member, which is impossible because the compiler does not know about it yet. So, by moving the definition of that constructor to the cpp-file, and after you have included the StrBlob.h header, then it will work fine because at that point, the compiler knows about the declaration of the StrBlob class. The same logic goes for the "begin" and "end" functions in the StdBlob class, i.e., the declaration based on the incomplete type is fine, but not the definition, which needs to be moved to the cpp file.

2) Headers should have self-sufficient inclusions, always. A header file should always include all the header it needs to include in order to form its declarations. So, if you use the standard std::string class in the declarations in a header, you need to include the <string> header to account for that. Headers are guarded, so it doesn't matter if that header was already included elsewhere already. It is very important that headers do not depend on the fact that some headers are "already included" somewhere else.

3) Headers should have self-sufficient forward declarations. The same logic as for inclusions applies to forward declarations too. If you need to use forward declarations in order to form the declarations in your header, then those forward declarations should be in your header, not somewhere else.

4) Don't use using namespace std;. You already did fairly good in not using that in the headers, but I would just recommend that habit of never using it at all.

5) Avoid leading underscores. You should not use things like _STR_BLOB_H_ because names starting with an underscore _ or two __ are reserved for the standard implementation and compiler. You should not get into the habit of using those, anywhere.

6) Avoid tabs. You used tabs everywhere for you indentation. That is bad practice because tabs are non-portable, sort of. The problem is that tabs are not necessarily the same size on different editors or operating system, and this could lead to messed-up indentation. Prefer to use spaces only, as I did.

7) The "linker1.h" header is completely unnecessary and you should, in general, avoid such indirect headers whenever possible. Basically, you were using this header to solve your problems which were mostly a result of not having applied rules 2 and 3 mentioned above. My code shows you the right way to do things.

If you compile this code with:

$ g++ -Wall -std=c++11 main.cpp StrBlob.cpp StrBlobPtr.cpp -o StrBlob.exe

it compiles successfully, as expected ;)

Hope this helps!

commented: great tips, and cleared up the things that I thought about the book, it seems the write doesn't even compile the code s/he wrote. +2

oh, btw, how about std::cout; std::endl; ,etc..

it's harmful too?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.