2010-06-09 25 views
6

Tôi đã xem một số related posts, nhưng tôi thực sự không thể hiểu được những gì tôi cần làm để sửa chương trình tôi đang tạo cho lớp C++ cấp nhập cảnh của mình.Lớp mẫu - Ký hiệu không được tìm thấy

lỗi của tôi là:

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 

Đây là những gì tôi đã có:

//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; 
} 

//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(); 
}; 

và Vector.cpp (mặc dù tôi đã cắt một số mã đó là hơi trùng lắp - các trình biến đổi & biến tần cho y và z, ví dụ)

//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))); 
} 

Vậy, cái quái gì đang xảy ra? Có phải vì tôi có tiêu đề riêng biệt & tệp .cpp cho Vector không? Làm thế nào để tôi nhận được chức năng chính của mình để nhận ra các chức năng của lớp Vector của tôi?

+1

Một nguồn thông tin là [C++ FAQ] (http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12), một [sách] tốt (http: // stackoverflow .com/questions/388242/the-definitive-c-book-guide-and-list) thích * "Mẫu - Hướng dẫn đầy đủ" * cũng sẽ hữu ích. –

Trả lời

10

Giải thích chi tiết có sẵn từ http://www.parashift.com/c++-faq-lite/templates.html

[35,12] Tại sao tôi không thể tách rời định nghĩa của lớp mẫu của tôi từ khai của nó và đặt nó bên trong một tập tin cpp?

Nếu tất cả những gì bạn muốn biết là cách khắc phục tình huống này, hãy đọc hai Câu hỏi thường gặp tiếp theo. Nhưng để hiểu tại sao mọi thứ lại như vậy, trước tiên hãy chấp nhận những sự kiện này:

  1. Mẫu không phải là một lớp hoặc chức năng. Mẫu là một mẫu "" mà trình biên dịch sử dụng để tạo ra một họ lớp hoặc các hàm.
  2. Để trình biên dịch tạo mã, nó phải xem cả định nghĩa mẫu (không chỉ khai báo) và các kiểu cụ thể/bất kỳ thứ gì được sử dụng để "điền vào" mẫu. Ví dụ, nếu bạn đang cố gắng sử dụng một Foo, trình biên dịch phải xem cả mẫu Foo và thực tế là bạn đang cố gắng tạo một Foo cụ thể.
  3. Trình biên dịch của bạn có thể không nhớ chi tiết của một tệp .cpp trong khi nó đang biên dịch một tệp .cpp khác. Nó có thể, nhưng hầu hết không và nếu bạn đang đọc FAQ này, nó gần như chắc chắn không. BTW này được gọi là "mô hình biên dịch riêng biệt."

Bây giờ dựa trên những sự kiện, đây là một ví dụ cho thấy tại sao mọi thứ theo cách mà họ đang Giả sử bạn có một mẫu Foo định nghĩa như thế này:.

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

Cùng với các định nghĩa tương tự cho các thành viên chức năng:

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

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

Bây giờ giả sử bạn có một số mã trong tập tin Bar.cpp sử dụng Foo:

// Bar.cpp

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

Rõ ràng ai đó ở đâu đó sẽ phải sử dụng "mẫu" cho định nghĩa constructor và cho someMethod() định nghĩa và nhanh chóng những khi T thực sự là int. Nhưng nếu bạn đã đặt định nghĩa của hàm tạo và someMethod() vào tệp Foo.cpp, trình biên dịch sẽ thấy mã mẫu khi nó biên dịch Foo.cpp và nó sẽ thấy Foo khi nó biên dịch Bar.cpp, nhưng sẽ không bao giờ có một thời gian khi nó nhìn thấy cả mã mẫu và Foo. Vì vậy, theo quy tắc số 2 ở trên, nó không bao giờ có thể tạo mã cho Foo :: someMethod().

Lưu ý cho các chuyên gia: Tôi rõ ràng đã thực hiện một số cách đơn giản ở trên. Điều này là cố ý vì vậy xin đừng phàn nàn quá lớn. Nếu bạn biết sự khác biệt giữa tệp .cpp và đơn vị biên dịch, sự khác biệt giữa một mẫu lớp và một lớp mẫu và thực tế là các mẫu thực sự không chỉ là các macro được vinh danh, thì đừng phàn nàn: câu hỏi/câu trả lời cụ thể này không nhắm vào bạn để bắt đầu. Tôi đơn giản hóa mọi thứ để người mới có thể "làm được", ngay cả khi làm như vậy xúc phạm một số chuyên gia.

[35.13] Làm cách nào để tránh lỗi liên kết với chức năng mẫu của tôi?

Cho trình biên dịch C++ của bạn biết ngay lập tức thực hiện khi biên dịch tệp .cpp của hàm mẫu của bạn.

Như một ví dụ, hãy xem xét foo.h tập tin tiêu đề, trong đó có miêu tả sau hàm mẫu:

// File "foo.h"

template<typename T> 
extern void foo(); 

Bây giờ giả sử tập tin Foo.cpp thực sự xác định rằng mẫu chức năng:

// file "Foo.cpp"

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

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

S uppose tập tin main.cpp sử dụng mẫu chức năng này bằng cách gọi foo():

// File "main.cpp"

#include "foo.h" 

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

Nếu bạn biên dịch và (cố gắng) liên kết các hai tập tin cpp, hầu hết trình biên dịch sẽ tạo ra lỗi liên kết. Có ba giải pháp cho việc này. Giải pháp đầu tiên là di chuyển vật lý định nghĩa của hàm mẫu vào tệp .h, ngay cả khi nó không phải là một hàm nội tuyến. Giải pháp này có thể (hoặc có thể không!) Gây ra bloat mã đáng kể, có nghĩa là kích thước thực thi của bạn có thể tăng đáng kể (hoặc, nếu trình biên dịch của bạn đủ thông minh, có thể không; thử và xem).

Giải pháp khác là để lại định nghĩa của hàm mẫu trong.tệp cpp và chỉ cần thêm dòng mẫu void foo(); đến tập tin đó:

// File "Foo.cpp"

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

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

template void foo<int>(); 

Nếu bạn không thể sửa đổi Foo.cpp, chỉ cần tạo một file cpp mới như foo-impl.cpp như sau:

// file "foo-impl.cpp"

#include "foo.cpp" 

template void foo<int>(); 

ý rằng foo-impl.cpp # bao gồm một tập tin cpp, không phải là một tập tin .h.

+0

Chúng ta làm gì với foo-impl.cpp? – ruipacheco

1

Có phải vì tôi có tiêu đề riêng biệt & .cpp tệp cho Vector?

Có.

Làm cách nào để nhận chức năng chính của tôi để nhận ra các chức năng của lớp Vector của tôi?

Đặt định nghĩa của mẫu chức năng và chức năng thành viên mẫu lớp vào tệp tiêu đề.

Có hiệu quả, định nghĩa mẫu cần phải có sẵn trong main.cpp để tạo mẫu với đối số bạn sử dụng (double, trong trường hợp này).

2

Giải pháp đơn giản nhất ở đây là bao gồm các tệp .cc/.cpp của bạn và có các tệp này bao gồm các tệp tiêu đề của chúng. Vì vậy, ví dụ: nếu chúng tôi có source.cppheader.h và chúng tôi muốn sử dụng tệp nguồn bên trong tệp main.cc, sau đó thêm #include "header.h" vào source.cpp#include "source.cpp" vào số main.cpp của bạn.

Không chắc liệu trình biên dịch có tối ưu hóa điều này hay không, nhưng điều này có hiệu quả đối với tôi.

+1

Tôi đã thử cách này và nó hoạt động. Giải pháp rất đơn giản – sourdesi

-4

sự cố không được giải quyết thực sự bằng cách bao gồm cpp thay vì tiêu đề. Mẫu và mẫu của nó không có cpp. tạo tiêu đề và lưu phần còn lại vào * .inl thay vì * .cpp. bao gồm inl ở cuối của * .h. điều này sẽ làm điều đó. Không # bao gồm bất kỳ thứ gì vào inl.

Các vấn đề liên quan