1

I am learning C++ and I am trying to create a "Online store" where you can publish articles and do comments about them, a comment belongs to an article and an article can have many comments but I don't now how to code it without creating a loop

I have tried using Article.h -> #include "Comment.h" and vice versa, but when I try to compile it creates a loop where A imports B and B imports A

Comment.h

#include <string>

//This is what I've tried but it creates a loop
#include "Article.h"


using namespace std;
//Class
class Comment
{
    private:
        Article article;
        string description;
        string rating;
};

Article.h


#include <string>
#include <map>

//The other part of my buggy-loop
#include "Comentario.h"

class Article
{
    private:
        map<string, int> art_comments;
        string name;
        string rating;
};

Update

Thank you everyone, my problem was solved with the Resolve build errors due to circular dependency amongst classes you gave me

As this question was solved ealier, shall I delete this post?

  • 6
    Recommended reading: [Resolve build errors due to circular dependency amongst classes](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) – user4581301 Jun 12 '19 at 15:44
  • 2
    You have already gone in one possible direction: Refer to the comments only by index. A variation of that would be storing pointers to `Comment`s (which will require reading the linked article). As it stands, there is no need to include `Commentario.h` in `Article.h` though, so just don't do that and it will be fine. – Max Langhof Jun 12 '19 at 15:45
  • That's not "spelling in code", that's vandalism: you removed the problem. – Lightness Races in Orbit Jun 12 '19 at 16:20
  • @LightnessRacesinOrbit I am sorry, I thought it would be helpful to let the readers look how my code was now and what was the new problem, And I didn't tag it as "spelling in code" StackOverflow did it for me – Graceless Dummy Jun 12 '19 at 16:34
  • The include file `Article.h` does not need to include `Comentario.h`. The `Article.h` file only references `std::map` and `std::string`. I recommend you using the `std::` prefix in header files rather than depending on some file having `using namespace std;`. Try to make header files self sufficient and not rely on other headers (except Standard Library). – Thomas Matthews Jun 12 '19 at 16:42
  • Possible duplicate of [Resolve build errors due to circular dependency amongst classes](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) – L. F. Jun 13 '19 at 10:20

2 Answers2

2

You need to go one step back.

You need first to analyze WHAT you want to do and then think of HOW you could do. Then you would do it (write the code) and then you would test it. In reality there are even more steps.

So let's start with the WHAT. You have some entities that have a relation. So you have

  • an "online store"
  • "articles" and
  • "comments" for the "articles"

That are your entities or objects. These entities have a relation

  • The online store has 0 or 1 or many articles
  • An existing article can have 0 or 1 or many comments

So, you want to store 0 or 1 or many "somethings". To store such "somethings", C++ offers containers in the STL. For example std::vector or std::list. You need to read about the properties of those containers and select the most fitting for you. In this case it would be most certainly a std::vector. It can contain 0 or more elements and can grow dynamically.

For the relations in C++ and object oriented languages mostly so called "has a" or "is a" properties are used. "is a" relations are modelled with derivations and "has a" types are models via so called containment.

Example:

#include <string>
#include <vector>

// "Is a"-relation. Done by deriving from base class Shape
struct Shape
{
  std::string name;
};
// A line is a shape
struct Line : public Shape
{
  int length;
};

// "Has a"-relation. Done by containment. Class online Shop contains articles
struct Article
{
  std::string articleName;
};
struct OnlineShop
{
  std::string shopName;
  std::vector<Article> article;
};


int main()
{
  Line line{ "long line",12345 };

  std::string myShopName{ "My Shop" };
  std::vector<Article> myArticles{ Article{ "Apple"}, Article{"Banana"} };
  OnlineShop myOnlineShop{ myShopName, myArticles };

  return 0;
}

Now you may understand a little better.

So for your solution, after thinking of the entities and the relations, you may come to a possible solution:

  • You will create one class OnlineStore
  • You will create a class Article
  • And a class Comment

Since there are possibly many Articles and many Comments, you will put those in a std::vector. And the class OnlineShop will conatin the class Article which will contain the class Comment.

As a result there will be no circular dependencies. Circular references are in most cases an indicator for a design flaw.

The below shows an skeleton of such implementation:

#include <string>
#include <vector>

struct Comment 
{
   std::string commentText{};
   std::string commentAuthor{};
};
struct Article
{
   std::string articleName{};
   std::vector<Comment> comment{};
};
struct OnlineShop
{
   std::vector<Article> article{};
};

int main()
{
   OnlineShop onlineShop{};
   return 0;
}

Please define your classes based on this approach. Then add the needed functionality.

Please also read about the principles of object oriented programming.

Hope this helps a little . . .

A M
  • 14,694
  • 5
  • 19
  • 44
2

That's a great question! Using a reference to an article along with forward declaration is probably what you're trying to do, though I don't see much of a reason for Comment to know anything about Article, especially since Article will probably own all of its Comments using std::vector< Comment>.

Right now, your comentario.h file looks like this:

// comentario.h
class Comment {
public:
  ...
private:
  Article article;
  [other implementation details for an Article]
}

Since your Comment definitely shouldn't own an Article, we can rewrite comentario.h this way:

// comentario.h
class Article; // this is a forward declaration in order to reference an Article

class Comment {
public:
  Comment(Article& article) // a Comment constructor--requires an Article
  ... // other Comment implementation details
private:
  Article& article;  // reference an existing Article
  ...
  // no need for Article implementation details at all in Comment
}

// comentario.cpp
#include "include/comentario.h"

#include "include/article.h" // we can include article.h now since we're outside the header

Comment::Comment(Article& article)  // the Comment constructor
: article(article) { // initialize the reference to an Article

}

That's one way to do this. I wouldn't personally give Comment any knowledge of Article, but if I had to, I'd create a read-only wrapper for Article details like the title and then reference that in Comment.

Side note: many programmers discourage "using namespace xx" in a header.

11 Equals
  • 83
  • 1
  • 10