0

The idea is this: I have SportsCar derived from Car, a Car consists of an Engine, each time Car.drive() is called, it calls Engine.consumeGas(), which in turn creates a Gas object and calls Gas.burn(). As you can see in the source code below.

Each class has its own header file and cpp file. And I wrote a Makefile (for Microsoft's NMAKE program).

The dependency is like this:

test.exe: main.obj Car.obj SportsCar.obj Engine.obj Gas.obj
main.obj: main.cpp Car.h SportsCar.h
Car.obj: Car.cpp Car.h
SportsCar.obj: SportsCar.cpp SportsCar.h // Here is what went wrong
Engine.obj: Engine.cpp Engine.h Gas.h
Gas.obj: Gas.cpp Gas.h

Build the program and run test.exe, produced the output:

Car drive

Engine consuming gas

Gas burning

SportsCar drive

Engine consuming gas

Gas burning

The problem is if I delete the member engine from Car and delete the call engine.consumeGas() in Car.drive() and rebuild the whole program, only Car.cpp and main.cpp are recompiled (SportsCar.cpp is not) and the linker won't complain at all.

After rebuilding, run test.exe get the output:

Car drive

SportsCar drive

Engine consuming gas

Gas burning

Apparently this result has completely violated the semantics of C++.

If I write the dependency like this, the problem will be fixed.

SportsCar.obj: SportsCar.cpp SportsCar.h Car.h

So if I have one source file A which includes a header file B, which in turn includes another header file C, and in A, the class C is used, I have to say A depends on both B and C, not just A depends on B?

If the project is large enough, I'm afraid I'll get lost trying to find out which file depends on which file while writing a Makefile.

Code:

main.cpp

#include "Car.h"
#include "SportsCar.h"
int main()
{
 Car *car = new Car();
 car->drive();
 delete car;
 car = new SportsCar();
 car->drive();
 delete car;
 return 0;
}

Car.h

#ifndef CAR_H
#define CAR_H
#include "Engine.h"
class Car
{
protected:
 Engine engine;
public:
 virtual void drive();
 virtual ~Car();
};
#endif // CAR_H

Car.cpp

#include "Car.h"
#include <iostream>
using namespace std;
void Car::drive()
{
 cout << "Car drive" << endl;
 engine.consumeGas();
}
Car::~Car()
{
 // do nothing
}

SportsCar.h

#ifndef SPORTSCAR_H
#define SPORTSCAR_H
#include "Car.h"
class SportsCar : public Car
{
public:
 void drive();
};
#endif // SPORTSCAR_H

SportsCar.cpp

#include "SportsCar.h"
#include <iostream>
using namespace std;
void SportsCar::drive()
{
 cout << "SportsCar drive" << endl;
 engine.consumeGas();
}

Engine.h

#ifndef ENGINE_H
#define ENGINE_H
class Engine
{
public:
 void consumeGas();
};
#endif // ENGINE_H

Engine.cpp

#include "Engine.h"
#include "Gas.h"
#include <iostream>
using namespace std;
void Engine::consumeGas()
{
 cout << "Engine consuming gas" << endl;
 Gas g;
 g.burn();
}

Gas.h

#ifndef GAS_H
#define GAS_H
class Gas
{
public:
 void burn();
};
#endif // GAS_H

Gas.cpp

#include "Gas.h"
#include <iostream>
using namespace std;
void Gas::burn()
{
 cout << "Gas burning" << endl;
}
asked Jan 18, 2015 at 15:56
9
  • When making makefile dependencies, you only need to look at the #include in your code. Commented Jan 18, 2015 at 16:01
  • Hm. This would be (fairly) simple with gmake and gcc/clang, but with nmake and msvc it's surprisingly painful. Do you have to use nmake? I think this would be easier with msbuild. Commented Jan 18, 2015 at 16:11
  • @brianbeuning What do you mean by "the #include in your code", which #include should I account in? Which #include I shouldn't? In my example above, SportsCar.cpp doesn't have a #include "Car.h", but it still has dependency on Car.h. Commented Jan 18, 2015 at 16:13
  • The compiler can generate the header dependencies from your code such it can be included in the makefile. At least GCC does this, using the options from the -MM option family. Commented Jan 18, 2015 at 16:18
  • @Wintermute you mean If I use GNU make this wouldn't be a problem? I haven't tried it yet. But I think it's reasonable for a building tool not to check the #include relation between files. I thought it would be a problem with GNU make, too. Commented Jan 18, 2015 at 16:20

1 Answer 1

1

You should generally never hardcode dependencies into makefiles. Instead, you should use the -M -MF flags to generate the dependencies during compilation and include the resulting file into your Makefile. Otherwise your dependencies will always be out of sync with reality.

Sadly, automatic dependency generation is a complex topic which I can't explain here in full detail. Many of the details can be found in this article: Auto-Dependency Generation

answered Jan 18, 2015 at 18:20
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. very useful answer. I'm reading this article, hopefully it will give me a nice clue of how to do it well.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.