2010-06-09 2 views
6

Ho visto alcuni related posts, ma non riesco davvero a capire cosa devo fare per correggere un programma che sto facendo per la mia classe C++ entry-level.Classe modello - Simboli non trovati

I miei errori sono:

Build Final Project of project Final Project with configuration Debug 

Ld "build/Debug/Final Project" normal x86_64 
cd "/Users/nick/Dropbox/|Syncs/Xcode/Final Project" 
setenv MACOSX_DEPLOYMENT_TARGET 10.6 
/Developer/usr/bin/g++-4.2 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk "-  L/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug" "-F/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug" -filelist "/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Final Project.build/Debug/Final Project.build/Objects-normal/x86_64/Final Project.LinkFileList" -mmacosx-version-min=10.6 -o "/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug/Final Project" 

Undefined symbols: 
    "Vector<double>::Vector()", referenced from: 
    _main in main.o 
    "Vector<double>::length()", referenced from: 
    _main in main.o 
    "Vector<double>::Vector(double const&, double const&, double const&)", referenced from: 
    _main in main.o 
    _main in main.o 
    "Vector<double>::getx() const", referenced from: 
    _main in main.o 
    _main in main.o 
    "Vector<double>::gety() const", referenced from: 
    _main in main.o 
    _main in main.o 
ld: symbol(s) not found 
collect2: ld returned 1 exit status 

Ecco quello che ho:

//main.cpp 

#include <iostream> 
#include "Vector.h" 

int main() { 
Vector<double> a(1, 2, 3); 
Vector<double> b(2, 4, 4); 
Vector<double> c; 

std::cout << "Length: " << b.length() << std::endl; 
std::cout << b.getx() << " ," << b.gety() << std::endl; 
std::cout << c.getx() << " , " << c.gety() << std::endl; 
return 0; 
} 

e

//Vector.h 

template <class T> 
class Vector { 
T x, y, z; 

public: 

//constructors 
Vector(); 
Vector(const T& x, const T& y, const T& z); 
Vector(const Vector& u); 

//accessors 
T getx() const; 
T gety() const; 
T getz() const; 

//mutators 
void setx(const T& x); 
void sety(const T& y); 
void setz(const T& z); 

//operations 
void operator-(); 
Vector plus(const Vector& v); 
Vector minus(const Vector& v); 
Vector cross(const Vector& v); 
T dot(const Vector& v); 
void times(const T& s); 
T length(); 
}; 

e il Vector.cpp (anche se ho rifilato un certo codice questo è in qualche modo duplice - i mittenti di accesso & per yez, per esempio)

//Vector.cpp 
#include "Vector.h" 
#include <iostream> 
#include <math.h> 

template class Vector<int>; 
template class Vector<double>; 

//default constructor 
template <class T> 
Vector<T>::Vector(): x(0), y(0), z(0) {} 


//constructor 
template <class T> 
Vector<T>::Vector(const T& x, const T& y, const T& z) 
{ 
setx(x); 
sety(y); 
setz(z); 
} 

//copy constructor 
template <class T> 
Vector<T>::Vector(const Vector& u) 
{ 
x = u.getx(); 
y = u.gety(); 
z = u.getz(); 
} 

//x accessor 
template <class T> 
T Vector<T>::getx() const 
{ 
return x; 
} 

//y accessor 

//z accessor 

//x mutator 
template <class T> 
void Vector<T>::setx(const T& x) 
{ 
Vector::x = x; 
} 

//y mutator 

//z mutator 

//negated Vector 
template <class T> 
void Vector<T>::operator-() 
{ 
setx(-this->getx()); 
sety(-this->gety()); 
setz(-this->getz()); 
} 

//sum 
template <class T> 
Vector<T> Vector<T>::plus(const Vector& v) 
{ 
Vector ret((getx() + v.getx()), (gety() + v.gety()), (getz() + v.getz())); 
return ret; 
} 

//difference 
template <class T> 
Vector<T> Vector<T>::minus(const Vector& v) 
{ 
Vector ret((getx() - v.getx()), (gety() - v.gety()), (getz() - v.getz())); 
return ret; 
} 

//cross product 
template <class T> 
Vector<T> Vector<T>::cross(const Vector& v) 
{ 
Vector ret; 
ret.setx(gety()*v.getz() - getz()*v.gety()); 
ret.sety(getz()*v.getx() - getx()*v.getz()); 
ret.setz(getx()*v.gety() - gety()*v.getx()); 
return ret; 

} 

//dot product 
template <class T> 
T Vector<T>::dot(const Vector& v) 
{ 
return (getx()*v.getx() + gety()*v.gety() + getz()*v.getz()); 
} 

//scalar times Vector 
template <class T> 
void Vector<T>::times(const T& s) 
{ 
setx(getx()*s); 
sety(gety()*s); 
setz(getz()*s); 
} 

//length of Vector 
template <class T> 
T Vector<T>::length() 
{ 
return sqrt((this->dot(*this))); 
} 

Quindi, che diavolo sta succedendo? È perché ho un header separato & .cpp file per Vector? Come posso ottenere la mia funzione principale per riconoscere le funzioni della mia classe Vector?

+1

Una fonte di informazioni è [Domande frequenti su C++] (http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12), un buon [libro] (http: // stackoverflow .com/questions/388242/the-definitive-c-book-guide-and-list) Mi piace anche * "Templates - The Complete Guide" *. –

risposta

10

Spiegazione dettagliata disponibile da http://www.parashift.com/c++-faq-lite/templates.html

[35.12] Perché non è possibile separare la definizione della mia classe template dalla sua dichiarazione e metterlo all'interno di un file cpp?

Se tutto quello che vuoi sapere è come risolvere questa situazione, leggi le prossime due domande frequenti. Ma per capire perché le cose sono come sono, prima accetta questi fatti:

  1. Un modello non è una classe o una funzione. Un modello è un "modello" che il compilatore utilizza per generare una famiglia di classi o funzioni.
  2. Per consentire al compilatore di generare il codice, è necessario visualizzare sia la definizione del modello (non solo la dichiarazione) e i tipi specifici/qualsiasi altro utilizzato per "compilare" il modello. Ad esempio, se stai cercando di usare un Foo, il compilatore deve vedere sia il modello di Foo sia il fatto che stai cercando di creare un Foo specifico.
  3. Probabilmente il compilatore non ricorda i dettagli di un file .cpp mentre sta compilando un altro file .cpp. Potrebbe, ma la maggior parte no e se stai leggendo queste FAQ, quasi sicuramente non lo fa. BTW questo è chiamato il "modello di compilazione separato."

Ora sulla base di tali fatti, ecco un esempio che mostra perché le cose sono come sono Supponiamo di avere un Foo modello definito in questo modo:.

template<typename T> 
class Foo { 
public: 
    Foo(); 
    void someMethod(T x); 
private: 
    T x; 
}; 

Insieme con le definizioni simili per gli Stati funzioni:

template<typename T> 
Foo<T>::Foo() 
{ 
    ... 
} 

template<typename T> 
void Foo<T>::someMethod(T x) 
{ 
    ... 
} 

Ora si supponga di avere un po 'di codice nel file di Bar.cpp che utilizza Foo:

// Bar.cpp

void blah_blah_blah() 
{ 
    ... 
    Foo<int> f; 
    f.someMethod(5); 
    ... 
} 

Chiaramente qualcuno da qualche parte è costretta a utilizzare il "modello" per la definizione del costruttore e per la definizione someMethod() e istanziare quelli in cui T è in realtà int. Ma se si fosse messo la definizione del costruttore e someMethod() nel file di Foo.cpp, il compilatore avrebbe vedere il codice modello quando si compilato Foo.cpp e sarebbe vedere Foo quando compilato Bar.cpp, ma non ci sarebbe mai stata un tempo in cui vedeva sia il codice del template che Foo. Quindi, con la regola # 2 sopra, non è mai stato possibile generare il codice per Foo :: someMethod().

Una nota agli esperti: ho ovviamente fatto alcune semplificazioni sopra. Questo era intenzionale quindi per favore non lamentarsi troppo forte. Se si conosce la differenza tra un file cpp ed un'unità di compilazione, la differenza tra un modello di classe e una classe template, e il fatto che i modelli in realtà non sono solo le macro glorificato, allora non si lamentano: questa particolare domanda/risposta non era rivolto a te per cominciare. Ho semplificato le cose così i neofiti avrebbero "capito", anche se così facendo offendono alcuni esperti.

[35.13] Come posso evitare errori del linker con le mie funzioni modello?

Dite al vostro compilatore C++ quali istanze effettuare mentre sta compilando il file .cpp della vostra funzione modello.

A titolo di esempio, si consideri il foo.h file di intestazione che contiene la dichiarazione seguente funzione template:

// File "foo.h"

template<typename T> 
extern void foo(); 

Ora supponiamo foo.cpp file definisce in realtà che funzione template:

// File "foo.cpp"

#include <iostream> 
#include "foo.h" 

template<typename T> 
void foo() 
{ 
    std::cout << "Here I am!\n"; 
} 

S uppose main.cpp file utilizza questa funzione template chiamando foo():

// file "main.cpp"

#include "foo.h" 

int main() 
{ 
    foo<int>(); 
    ... 
} 

Se si compila e (cercare di) collegare questi due file cpp, la maggior parte i compilatori genereranno errori di linker. Ci sono tre soluzioni per questo. La prima soluzione è quella di spostare fisicamente la definizione della funzione modello nel file h, anche se non è una funzione inline. Questa soluzione potrebbe (o potrebbe non!) Causare un significativo aumento di codice, il che significa che la dimensione dell'eseguibile potrebbe aumentare notevolmente (oppure, se il compilatore è abbastanza intelligente, potrebbe non farlo; provalo e guarda).

L'altra soluzione è quella di lasciare la definizione della funzione modello nella.file cpp e aggiungi semplicemente il modello di linea void foo(); a quel file:

// File "foo.cpp"

#include <iostream> 
#include "foo.h" 

template<typename T> void foo() 
{ 
    std::cout << "Here I am!\n"; 
} 

template void foo<int>(); 

Se non è possibile modificare foo.cpp, è sufficiente creare un nuovo file cpp come foo-impl.cpp come segue:

// File "foo-impl.cpp"

#include "foo.cpp" 

template void foo<int>(); 

noti che foo-impl.cpp # include un file cpp, non un file .h.

+0

Cosa facciamo con foo-impl.cpp? – ruipacheco

1

È perché ho un'intestazione separata & .cpp file per Vector?

Sì.

Come si ottiene la funzione principale per riconoscere le funzioni della classe Vector?

Inserire le definizioni dei modelli di funzione e delle funzioni membro del modello di classe nel file di intestazione.

In effetti, la definizione del modello deve essere disponibile in main.cpp per consentire l'istanziazione del modello con gli argomenti utilizzati (double, in questo caso).

2

La soluzione più semplice è quella di includere i file .cc/.cpp e includere questi file nell'intestazione. Ad esempio, se abbiamo source.cpp e header.h e vogliamo utilizzare il file sorgente all'interno del file main.cc, quindi aggiungere #include "header.h" a source.cpp e #include "source.cpp" al numero main.cpp.

Non sono sicuro se il compilatore ottimizzerà questo, ma questo ha funzionato per me.

+1

Ho provato questo e ha funzionato. Soluzione molto semplice – sourdesi

-4

il problema non viene risolto davvero includendo il cpp al posto dell'intestazione. È un modello e i modelli non hanno un cpp. crea un'intestazione e memorizza il resto in * .inl invece che in * .cpp. includi l'inl alla fine del tuo * .h. questo lo farà. Non includere nulla in the inl.