C++ #include Loop

2019-07-24 16:00发布

File Dog.h

#ifndef DOG_H
#define DOG_H

#include "Cat.h"
//class Cat;

class Dog
{
        Cat c;
};

#endif

File Cat.h

#ifndef CAT_H
#define CAT_H

#include "Dog.h"
//class Dog;

class Cat
{
        Dog d;
};

#endif

This causes a loop, the problem is that both classes needs to know about each other; forward declaration doesn't solve the problem either. You can create a pointer of either Dog d or Cat c to solve it.

Question: Is there really no way of not creating a pointer, i.e keep the code as it is without rewrite of how I want it to be?

4条回答
可以哭但决不认输i
2楼-- · 2019-07-24 16:09

Besides this leading to an infinite space object (as Raphael explains), the compiler won't let this happen as explained in this question. He doesn't know about Cat class when you are using it in Dog class, so it won't work for the same reason that this won't work :

class B;
class A{
    B b;
};
查看更多
女痞
3楼-- · 2019-07-24 16:15

No, this is not possible, and it has nothing to do with include files.

The reason is that if you create a class which contains another class as a member (rather than a pointer), the compiler needs to add some space for that class into the other class. E.g., if you create something like this:

class Dog {
  std::string name;
  std::string owner;
};

class Person {
    Dog pet;
    std::string name;
};

Then instances of the class Person in the above example have a size that is sizeof(std::string) + sizeof(Dog), while instances of the class Dog have a size that is sizeof(std::string) * 2; so Person is sizeof(std::string) * 3 in size (modulo alignment differences)

In other words, the memory required for a first-class variable (as opposed to a pointer) in C++ is assigned as part of the instance, and is not a pointer; this works very much the same way as structs do. In fact, there really is no practical difference between the class and struct keywords in C++, other than the fact that a class is private by default, whereas a struct is public.

查看更多
混吃等死
4楼-- · 2019-07-24 16:16

What you're trying to do is impossible. Your class would take infinite space.

You got a Dog which has a Cat member variable, which has a Dog member variable, which has a Cat member variable, which has a Dog member variable...

Without breaking it somewhere with a pointer you'll end up with infinite recursive member variables taking infinite memory which is not possible.

Note that the compiler can't even actually calculate the memory needed for Dog or Cat due to the infinite recursion. But since a class in C++ can't be 0 size normally, we know that it'll be infinite space anyways.

The other answers give nice perspectives too, i.e. 'Chicken and Egg' problem of which one has to be defined first to be able to define the other.


I just wanna add that mutually recursive classes are something strange and best avoided if you can design it some other way. If you really have to use them make sure that you actually break the recursiveness. Otherwise you shift it only from an error at compile time due to infinite mutual recursion to running out of memory and an crash at runtime if you got a naive implementation with pointers that does infinite mutually recursive new calls at construction time.

查看更多
贼婆χ
5楼-- · 2019-07-24 16:24

As already pointed out, you cannot directly or indirectly define an object that has itself as a member without indirection.

If you only want the ability to use the . syntax instead of the -> syntax, you can use a reference instead of a pointer. For example:

// in Dog.h
class Cat;

class Dog {
public:
    std::unique_ptr<Cat> catp;
    Cat &c;
    Dog ();
    Dog (Cat &);
};

// in Cat.h
class Cat {
public:
    std::unique_ptr<Dog> dogp;
    Dog &d;
    Cat ();
    Cat (Dog &);
}

Now, in your source code, you can do something like this:

#include "Dog.h"
#include "Cat.h"

Dog::Dog () : catp(new Cat(*this)), c(*catp) {}
Cat::Cat () : dogp(new Dog(*this)), d(*dogp) {}

Dog::Dog (Cat &cat) : c(cat) {}
Cat::Cat (Dog &dog) : d(dog) {}
查看更多
登录 后发表回答