Compiler says class has no constructor while it actually has one

Started by
19 comments, last by lucky6969b 10 years, 10 months ago
Not a bug, but what happened here?

    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Advertisement

Not a bug, but what happened here?


    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }

Default constructor sets Actor and Target to NULL?

OP, you need to include objects.h and activity.h in idle.cpp

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

Not a bug, but what happened here?


    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }

Default constructor sets Actor and Target to NULL?

OP, you need to include objects.h and activity.h in idle.cpp

Thank you for that... rolleyes.gif

There's two constructors there. I was asking why he uses initializers in one and manual assignments in the other.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Not a bug, but what happened here?


    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }

Default constructor sets Actor and Target to NULL?

OP, you need to include objects.h and activity.h in idle.cpp

Thank you for that... rolleyes.gif

There's two constructors there. I was asking why he uses initializers in one and manual assignments in the other.

My guess is the assumption that only constants can be used in initializer lists.

The hierarchy is as follows

Objects.h includes Idle.h

Idle.h includes activity.h

The problem I am having is Objects would instance Idle, which derives from Activity

And Idle has a pointer referencing Objects as well in turn.

Would that be a problem if I instance Idle in the header file called Objects.h, not in the implementation file as well

Thanks

Jack

Not a bug, but what happened here?


    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }

Default constructor sets Actor and Target to NULL?

OP, you need to include objects.h and activity.h in idle.cpp

Thank you for that... rolleyes.gif

There's two constructors there. I was asking why he uses initializers in one and manual assignments in the other.

What is the general practice when confronting with this situation?

Thx

Jack

What is the general practice when confronting with this situation?

 Activity( Objects* actor, Goods* target ) : Actor(actor), Target(target)
 {
 }
As for your compile errors:

It seems you have a misunderstanding about compilation units and forward-declaration in general here. From your errors, for example...
error C2027: use of undefined type 'Objects'    D:\Jacky\Documents\Visual Studio 2010\Projects\PerfectSim\PerfectSim\PerfectSim\Activities\Idle.cpp    16    1    PerfectSim
...the Idle.cpp compilation unit has no idea what the Objects class looks like; which is, of course, because Idle.cpp only #includes Idle.h. Idle.h only #includes Activity.h. Neither of these include Objects.h, so the compiler has no way of knowing what Objects looks like, what members or methods are accessible, or even that it exists as a class.

Forward declaration is used to inform the compiler that there is a class called Object, but it still doesn't give the compiler any indication of what that class looks like. It is sufficient to forward declare a class in a header if, for example, you use a pointer to that class somewhere in another class definition; but if you actually want to call any methods or reference any members of that class, then a forward declaration isn't sufficient and the compiler will, at that point, need the full class declaration from the class header to be included.

Now, you say that Object.h includes Idle.h, which includes Activity.h, so including Object.h into the Idle.cpp compilation unit would cause a problem, since Idle.h is already explicitly included, resulting in a multiple declaration. Removing the include of Idle.h and just including Object.h instead would solve it, but problems of multiple definition like this are why people commonly use what are called include guards, either the pre-processor directive #pragma once or an #ifndef structure like this:
#ifndef IDLE_H
#define IDLE_H
// Class declaration here
#endif
Using pre-processor directives like this means that you can safely include a header multiple times in a compilation unit, but it will only actually be included once. If Activity.h, Idle.h and Object.h were all properly equipped with include guards, then you can safely include them wherever needed, without worrying about redeclaration errors.

Watch out for circular dependencies. If you have something like this:
// Foo.h
#ifndef FOO_H
#define FOO_H
#include "Bar.h"

class Foo
{
  public:
  Bar bar_;
};
#endif
// Bar.h
#ifndef BAR_H
#define BAR_H
#include "Foo.h"

class Bar
{
  public:
  Foo foo_;
};
#endif
then you have a problem. This sort of code structure is not allowed. If you study the nesting you should see why. Foo contains an instance of Bar, which contains an instance of Foo, which contains an instance of Bar, and so on until out-of-memory exception, infinity, or the heat-death of the universe, whichever comes first. It is, however, legal to achieve the same with pointers, as you seem to be doing despite your statements that Objects.h instanced Idle:
// Foo.h
#ifndef FOO_H
#define FOO_H

#include "Bar.h"

class Foo
{
  public:
  Bar *bar_;
};

#endif
//Bar.h
#ifndef BAR_H
#define BAR_H

#include "Foo.h"

class Bar
{
  public:
  Foo *foo_;
};
#endif
Doing it this way is okay. Note that if you didn't have include guards, though, it would fail because including Foo.h would also include Bar.h which would in turn include Foo.h, which would again include Bar.h, and so on, yada yada yada, heat death. But the include guards mean that once the set of declarations enclosed by the guards have been processed, they won't be processed again, so including Foo.h will include Bar.h, which will try to include Foo.h but since it has already been included it amounts to a no-op and Bar.h will not be included again.

This, however, is not really recommended. If the class Foo is only ever referred to in pointer definitions in Bar.h, and Bar is only ever referred to in pointer definitions in Foo.h, then there is no need for either header to explicitly include the other. This is where forward declarations come in:
// Foo.h
#ifndef FOO_H
#define FOO_H

class Bar;
class Foo
{
  public:
  Bar *bar_;
};
#endif
// Bar.h
#ifndef BAR_H
#define BAR_H

class Foo;
class Bar
{
  public:
  Foo *foo_;
};
#endif
If nothing inside the Foo.h header needs to know what Bar looks like (ie, what methods or members it has) but just has to be able to deal with a pointer to Bar, then it is preferred to merely forward declare Bar rather than explicitly include Bar.h. This removes a compile-time dependency between the two modules. If the two headers explicitly include each other, and a change is made to one header, then any compilation unit where either of those headers is included will end up being re-compiled even if it wouldn't normally be necessary. By removing the explicit inclusion and using forward declaration instead, you can skip some needless recompiling. Note, however, that if the Foo.cpp compilation unit makes any kind of use of the Bar pointer member (ie, using it to call any Bar methods or access any Bar members) then Foo.cpp will need to include Bar.h so that the compiler knows what it looks like. Similarly with Bar.cpp including Foo.h.

Forward declaration is also useful for setting up circular pointer-based dependencies that occur within the same header:
// FooBar.h
#ifndef FOOBAR_H
#define FOOBAR_H

class Bar;
class Foo
{
  public:
  Bar *bar_;
};

class Bar
{
  public:
  Foo *foo_;
};

In this example, when Foo is declared, Bar hasn't yet been declared so the only way Foo has of knowing that Bar is even a class type is through the forward declaration of Bar.

Now, say you want to change the pointer to Bar that Foo holds into an actual instance of Bar:
// FooBar.h
#ifndef FOOBAR_H
#define FOOBAR_H

class Bar;
class Foo
{
  public:
  Bar bar_;
};

class Bar
{
  public:
  Foo *foo_;
};

#endif
This won't work, because since Foo is dealing with an instance of Bar rather than just a pointer to it, forward declaration is no longer sufficient. The compiler needs to know what Bar looks like, in order to know how big a piece of memory to allocate for it inside the Foo memory structure. Similarly, if any inlined functions declared in the class declaration of Foo call bar_'s methods, then forward declaration is no longer sufficient. This won't work:
// FooBar.h
#ifndef FOOBAR_H
#define FOOBAR_H

class Bar;
class Foo
{
  public:
  Bar *_;

  inline void baz()
  {
    if(bar_) bar_->qux();
  }
};

class Bar
{
  public:
  Foo *foo_;

  void qux();
};
#endif
At the point of defining the method Foo::baz, the inline function definition explicitly calls the method Bar::qux, meaning that at that point the compiler will need to know what Bar looks like, and the forward declaration isn't sufficient. In this case you'll probably want to forego defining any methods, inline or otherwise, inside the header file and put them in the compilation unit instead. Granted, reversing the order that Foo and Bar appear, and forward declaring Foo for Bar's declaration, would solve the issue but it wouldn't solve the problem presented here:
// FooBar.h
#ifndef FOOBAR_H
#define FOOBAR_H

class Bar;
class Foo
{
  public:
  Bar * bar_;

  inline void baz()
  {
    if(bar_) bar_->qux();
  }
};

class Bar
{
  public:
  Foo *foo_;

  inline void qux()
  {
    if(foo_) foo_->baz();
  }
};
#endif
... which, ignoring for a moment the infinite recursion loop, simply wouldn't compile given the insufficiency of the forward declaration.

So just remember: use forward declaration if you only need to know if a class exists but don't care what it looks like, but explicitly include the class header file if you need to know what it looks like. Use include guards to prevent multiple declarations of classes. And sorry for the long post; it kind of got away from me.




Not a bug, but what happened here?


    Activity() : Actor(0), Target(0) { }
    Activity( Objects* actor, Goods* target )
    {
        Actor = actor;
        Target = target;
    }
Default constructor sets Actor and Target to NULL?

OP, you need to include objects.h and activity.h in idle.cpp

Thank you for that... rolleyes.gif

There's two constructors there. I was asking why he uses initializers in one and manual assignments in the other.

What is the general practice when confronting with this situation?
Thx
Jack
Always use the initializer list, unless you have a specific reason NOT to. The quoted code above does not have any reason not to use it so both constructors should use the initializer list.

One error left.


activity = new Idle(this, NULL);

Error 1 error C2514: 'Idle' : class has no constructors d:\jacky\documents\visual studio 2010\projects\perfectsim\perfectsim\perfectsim\Objects\Objects.h 44 1 PerfectSim

The strange thing was I already had my constructor set in place.


#pragma once

#include "Activity.h"
#include "..\Objects\Objects.h"
 
 
 
 

#define MAX_IDLE_TIME 8
#define ACTIVITY_IDLE_ENERGY 1.0

class Objects;
class Activity;

class Idle : public Activity
{
private:
    float mTimeInIdle;

public:
    Idle() : mTimeInIdle(0)    { }
    Idle(Objects *actor, Goods *target) : Activity(actor, target) { }

    bool OnUpdate(float seconds);
    

};

The previous errors have been fixed after adding the pragma directive.

Thank the guys who helped.

Jack

If the line activity = new Idle(this, NULL); occurs inside Objects.h, as seems to be indicated by the info in the error message, then it's probably because where that line occurs there has only been a forward declaration of Idle. Go read what JTippetts posted again. If you want to use anything that is part of the class Idle (including constructors) then before you do so you have to include the full declaration of Idle. If the compiler encounters new Idle, and it still doesn't know what the fuck Idle looks like because all it has to work from is a forward declaration, then you're going to get that error. Whenever you have cyclical-dependency fuckery, you would probably be better off not putting any executable code at all inside your headers and keep it all to .cpp files.

This topic is closed to new replies.

Advertisement