C++ C++ undefined refernece error to

XHotSniperX

Lt. Junior Grade
Registriert
Jan. 2008
Beiträge
472
Hey Leute
Ich bekomme diesen Fehler bei dem kleinen C++ Programm, wenn ich Main.cpp mit "g++ -Wall -pedantic Main.cpp" kompiliere. Hier sind alle Dateien. Der Fehler lautet: Main.cpp:(.text+0x1c): undefined reference to `BMW::BMW()'. Wisst ihr, was den Fehler verursacht?


Main.cpp

Code:
    #include <iostream>
    #include "BMWLogo.h"
    #include "Engine.h"
    #include "IVehicle.h"
    #include "Car.h"
    #include "BMW.h"
    
    int main() {
    	BMW* bmw = new BMW();
    	Car* car = bmw;
    	std::cout << car->getName() << std::endl;
    }

IVehicle.h

Code:
    class IVehicle {
    	public:
    		IVehicle();
    		virtual std::string getName();
    		virtual float getCurrentSpeed();
    };

IVehicle.cpp

Code:
    #include "IVehicle.h"

    IVehicle::IVehicle() {

    }
    virtual std::string IVehicle::getName() {
    	
    }
    virtual float IVehicle::getCurrentSpeed() {
    
    }

Car.h

Code:
    class Car : public IVehicle {
    
    	private:
    		std::string name;
    		float currentSpeed;
    		Engine* engine;
    	public:
    		Car(std::string name);
    	
    		void setCurrentSpeed(float currentSpeed);
    
    		float getCurrentSpeed();
    	
    		std::string getName();
    	
    };

Car.cpp

Code:
    #include "Car.h"
    
    Car::Car(std::string name) {
    	this->name = name;
    	engine = new Engine();
    }
    
    void Car::setCurrentSpeed(float currentSpeed) {
    	this->currentSpeed = currentSpeed;
    }
    
    float Car::getCurrentSpeed() {
    	return currentSpeed;
    }
    	
    std::string Car::getName() {
    	return name;
    }

BMW.h

Code:
    class BMW : public Car {
    	private: 
    		BMWLogo* bmwLogo;
    	public:
    		BMW();
    };

BMW.cpp

Code:
    #include "BMW.h"
    
    BMW::BMW()
    : Car("BMW") {
    	bmwLogo = new BMWLogo();
    }

Engine.h

Code:
    class Engine {
    
    	Engine();
    
    };

Engine.cpp

Code:
    #include "Engine.h"
    
    Engine::Engine() {
    
    }

BMWLogo.h

Code:
    class BMWLogo {
    
    	BMWLogo();
    
    };

BMWLogo.cpp

Code:
    #include "BMWLogo.h"
    
    BMLogo::BMWLogo() {
    
    }
 
Hast Du schon Breakpoints gesetzt und anschließend den Debugger genutzt um durch den Code zu springen?
 
Zuletzt bearbeitet von einem Moderator:
Hmm ich weiss nicht, wie ich im Terminal mit Breakpoints arbeiten kann :) Ich mach das gerade auf Ubuntu mit dem Terminal. Hab leider kein IDE.
 
Wenn du den Compiler so aufrufst, dann impliziert dieser Aufruf auch den Linker. Du musst in diesem Fall jede Datei einzeln übersetzen und zum Schluss alle Objektdateien linken, z.B:
Code:
// erzeugt main.o
g++ -c main.cpp
// erzeugt dingdong.o
g++ -c dingdong.cpp
// linken
g++ main.o dingdong.o

Der Vorgang wird früher oder später etwas nervig. Hier werden dann Makefiles praktisch...
 
Skamander schrieb:
Bist Du schon mit dem Debugger durch den code gesprungen und hast Breakpoints gesetzt?

Oh man. Du hast ja mal so KEINE AHNUNG was du da schreibst. Wieso antwortest du denn zu einem Thema, von dem du absolut keine Ahnung hast?

Zum Thema:
Das kann auch so nicht funktionieren, weil du nur das Main Programm übersetzt aber der Compiler aber auch IVehicle.cpp, Car.cpp, BMW.cpp ... mit übersetzen muss. Denn so weiß der Linker nicht woher er den Konstruktor von der Klasse BMW herholen soll.
Du solltest dir erst mal nochmal die Urbasic von Compiler, Linker und Klassen angucken.
Übersetzen müsstest du es so können:

g++ -Wall -pedantic Main.cpp IVehicle.cpp (... die restlichen cpp Dateien) -o Programmname
 
GrinderFX schrieb:
[...] Wieso antwortest du denn zu einem Thema, von dem du absolut keine Ahnung hast?

Die Frage sah nach einer typischen Hausaufgabenfrage aus bei der keine Zeit damit verbracht wurde nach einer Lösung zu suchen. Da hab ich einfach mal Dünschiss rausgehauen ... :) ... schieb ich einfach mal darauf das ich schon den ganzen Tag genau so 'ne Fragen in allen Foren in denen ich unterwegs bin gesehen habe.

Obwohl ich keinen Plan von c++ habe genügt es nach der Fehlermeldung zu suchen um gleich auf der Startseite Lösungsvorschläge zu finden... Scheint ja nicht gerade selten zu sein der Fehler.
 
Und weil du keine Ahnung hast antwortest du mit irgendeiner sinnlosen Antwort, die gar nichts mit der Frage zutun hat?
 
GrinderFX schrieb:
Und weil du keine Ahnung hast antwortest du mit irgendeiner sinnlosen Antwort, die gar nichts mit der Frage zutun hat?

So sinnlos ist die Antwort gar nicht. Die Fehler denen ich in Foren in der Regel über den Weg laufe sind nahezu immer zu beheben indem man Breakpoints setzt und per Debugger durch den Code springt. Nur nutzen diese Möglichkeit die User oftmals gar nicht, ist ja einfacher in einem Forum zu fragen. Hätte hier auch passen können. Hat es nicht. Shit happens.

Genau betrachtet hat der Threadstarter sogar was lernen können - auch vom Terminal aus kann man debuggen. :) :freak:
 
Skamander schrieb:
Genau betrachtet hat der Threadstarter sogar was lernen können - auch vom Terminal aus kann man debuggen. :) :freak:

Nein, wenn der Code erst gar nicht kompiliert, kannst du da nix debuggen.

Zum Fehler:
undefined reference bedeutet immer, dass der Linker ein Symbol nicht finden kann. Ein Symbol kann entweder eine statische Variable oder eine Funktion oder eine Klassenreferenz sein (hab ich etwas vergessen?).

D.h. überprüfen, ob du eine statische Variable irgendwo vergessen hast, einen Header irgendwo benötigst, oder eine Funktion schlichtweg nicht implementiert hast.
 
Kanibal schrieb:
Nein, wenn der Code erst gar nicht kompiliert, kannst du da nix debuggen.
[...]

Die Antwort [1] war darauf bezogen das der Autor nicht wusste wie man im Terminal mit Breakpoints arbeiten kann. Meine erste Antwort war wie weiter oben geschrieben [2] Dünnschiss. So gesehen hat er trotz meines Dünschisses in der ersten Antwort aber noch eine Möglichkeit aufgezeigt bekommen wie man im Terminal debuggen könnte - sofern das Programm kompiliert. Darauf wollte ich hinaus...

[1] https://www.computerbase.de/forum/threads/c-undefined-refernece-error-to.1206491/#post-13914642
[2] https://www.computerbase.de/forum/threads/c-undefined-refernece-error-to.1206491/#post-13914859
 
Kanibal schrieb:
undefined reference bedeutet immer, dass der Linker ein Symbol nicht finden kann. Ein Symbol kann entweder eine statische Variable oder eine Funktion oder eine Klassenreferenz sein (hab ich etwas vergessen?).

Mir fallen noch globale Variablen (mit external linkage) ein, welche im header-file nur deklariert sind:
extern int X;

Statische Variablen per se gibts eigentlich nicht. Bei static class data members hast du recht, die müssen wo definiert sein. Auf namespace-level haben als static deklarierte Variablen internal linkage (zumindest in C++98 / 03) und ich wüsste nicht, wie man die nur deklarieren (ohne auch gleich zu definieren) kann.
 
Vielen Dank für die Tipps mit den Terminalbefehlen. Die Zwischenschritte mit den Quelldateien, Objektdateien und dem ausführbaren fertigen Programm hatte ich zwar noch im Kopf aber ich wusste noch nicht genau, wie man das von Hand im Terminal kompiliert und zusammenfügt oder ob das automatisch gemacht wird.

Jetzt weiss ich, dass es nicht automatisch gemacht wird hehe. Hier sind noch die etwas korrigierten Quelldateien, die man mit
g++ -Wall -pedantic Main.cpp BMW.cpp Car.cpp BMWLogo.cpp IVehicle.cpp Engine.cpp -o Programmname
kompilieren kann.

Main.cpp
Code:
#include <iostream>
#include "BMW.h"

int main() {
	BMW* bmw = new BMW();
	Car* car = bmw;
	std::cout << car->getName() << std::endl;
}

BMW.h
Code:
#include "Car.h"
#include "BMWLogo.h"

class BMW : public Car {
	private: 
		BMWLogo* bmwLogo;
	public:
		BMW();
};

BMW.cpp
Code:
#include "BMW.h"

BMW::BMW()
: Car("BMW") {
	bmwLogo = new BMWLogo();
}

Car.h
Code:
#include "IVehicle.h"
#include "Engine.h"

class Car : public IVehicle {

	private:
		std::string name;
		float currentSpeed;
		Engine* engine;
	public:
		Car(std::string name);
	
		void setCurrentSpeed(float currentSpeed);

		float getCurrentSpeed();
	
		std::string getName();
	
};

Car.cpp
Code:
#include "Car.h"

Car::Car(std::string name) {
	this->name = name;
	engine = new Engine();
}

void Car::setCurrentSpeed(float currentSpeed) {
	this->currentSpeed = currentSpeed;
}

float Car::getCurrentSpeed() {
	return currentSpeed;
}
	
std::string Car::getName() {
	return name;
}

BMWLogo.h
Code:
class BMWLogo {
	public:
		BMWLogo();

};

BMWLogo.cpp
Code:
#include "BMWLogo.h"

BMWLogo::BMWLogo() {

}

IVehicle.h
Code:
#include <string>

class IVehicle {
	public:
		IVehicle();
		virtual std::string getName();
		virtual float getCurrentSpeed();
};

IVehicle.cpp
Code:
#include "IVehicle.h"

IVehicle::IVehicle() {

}
std::string IVehicle::getName() {
	
}
float IVehicle::getCurrentSpeed() {

}

Engine.h
Code:
class Engine {
	public:
		Engine();

};

Engine.cpp
Code:
#include "Engine.h"

Engine::Engine() {

}
 
Zuletzt bearbeitet:
Und das kompiliert auch ohne Fehler? Mich wundert zum Beispiel schon, dass deine IVehicle::getName() und IVehicle::getCurrentSpeed() Methoden ja laut Methodensignatur eigentlich was zurückgeben müßten, es aber nicht tun. Da sollte ein gescheiter Compiler eigentlich meckern.

Mal noch der eine oder andere Hinweis von mir.

1. Bist du bereits mit dem const-Schlüsselwort vertraut? Deine getter-Methoden (getName(), getCurrentSpeed()) könnte man wahrscheinlich ohne weiteres const machen. Also:

Code:
	virtual std::string getName() const;
	virtual float getCurrentSpeed() const;

Das const am Ende der Methode bedeutet, "diese Methode verspricht, an dem Objekt, an dem sie aufgerufen wird, nichts zu verändern" ... und bei simplen getter-Methoden ist das gemeinhin ja ein sinnvolles Versprechen.

2. Das ist in diesem Fall zwar nur ein unwichtiges Thema, aber deine main()-Funktion hat ein kleines Memory-leak, da du mit BMW* bmw = new BMW(); ein Objekt auf dem Freestore allokierst, es aber später nicht mit delete wieder wegräumst. Zwar wird bei Programmende jedes übliche Desktop-OS sowieso dafür sorgen, daß der vom Programm benutzte Speicher wieder freigegeben wird, aber sauberer wäre es trotzdem, wenn du es sauber wieder wegräumst. Allein schon deshalb, weil sonst auch nicht der Destruktor deines Objektes aufgerufen wird. Das mag in diesem Fall zu verschmerzen sein, aber du könntest ja auch einen komplexen Destruktor geschrieben haben, der irgend was furchtbar wichtiges tut. Da würdest du dann bestimmt auch wollen, dass der auch wirklich ausgeführt wird.

3. Der Konstruktor von Car sollte sein std::string-Argument nicht per-value sondern per-const-reference empfangen. Somit wird eine unnötige Kopie eingespart. Moderne Compiler können die häufig sowieso wegoptimieren, aber warum soll man sich darauf verlassen, wenn man es auch gleich richtig tun kann?

Code:
Car::Car(const std::string& name) {
	this->name = name;
	engine = new Engine();
}

4. Das name-Member deiner Car-Klasse solltest du initialisieren, nicht zuweisen. Dazu nutzt man in C++ Initialisierungslisten.

Code:
Car::Car(const std::string& name) : name( name )
	engine = new Engine();
}

5. Deinen currentSpeed-Member solltest du ebenfalls auf einen sinnvollen Ausgangswert initialisieren.

Code:
Car::Car(const std::string& name) : name( name ), currentSpeed( 0.0f )
	engine = new Engine();
}

6. Deine Car-Klasse sollte auch einen Destruktor bekommen, in dem du die Engine-Instanz, die du im Konstruktor angelegt hast, wieder zerstörst (sonst -> Memoryleak).

Code:
Car::~Car()
{
	delete engine;
}

7. Du solltest den Kopier-Konstruktor und den Kopier-Zuweisungs-Operator deiner Car-Klasse unbenutzbar machen, denn so wie deine Car-Klasse implementiert ist, hätte das Kopieren einer Car-Instanz katastrophale Effekte. Zum einen würde es zu einem Memoryleak kommen, und zum zweiten würde die selbe Engine-Instanz von mehr als einem Car-Destruktor gelöscht werden. Wenn dein Compiler schon voll C++11-fähig ist, kannst du den Kopier-Konstruktor und den Kopier-Zuweisungs-Operator wie folgt abschalten:

Code:
Car( const Car& ) = delete;
Car& operator = ( const Car& ) = delete;

Wenn dein Compiler diesen Teil von C++11 noch nicht unterstützt, kannst du das gleiche erreichen, indem du beide als private deklarierst und sie dann (ganz wichtig) nicht implementierst (also ihnen keinen Rumpf verpaßt).
 
Zuletzt bearbeitet:

Ähnliche Themen

Zurück
Oben