Lỗi khi định nghĩa cho class template trong file .cpp và cách khắc phục

Lỗi gặp khi làm bài tập xây dựng stack, mình có file header như sau:

// stack.h 

template <typename T>
class NStack
{
private:
    T * elem;
    int sz;
public:
    NStack();

    // declaring other methods
};

Và file .cpp:

// stack.cpp
#include "stack.h"
#define MAX_STACK_SIZE 100
template <typename T>
NStack<T>::NStack() 
: elem { new T[ MAX_STACK_SIZE ] },
  sz { 0 }
{}

// defining other methods

Ở chương trình chính, khi bạn khai báo NStack<int> s chẳng hạn, và build thì chương trình sẽ báo lỗi:

Undefined symbols for architecture x86_64:”NStack<int>::NStack()”, referenced from:
_main in exe3.o
ld: symbol(s) not found for architecture x86_64

Đây là một lỗi gặp khi link, compiler không tìm được phần define của constructor NStack<int>::NStack()

Nguyên nhân là do: Một template class, không phải là một class, mà nó chính xác là một “khuôn mẫu” dùng để tạo ra class. Khi ta khai báo NStack<int> s; ta phải chỉ rõ tất cả các template member functions để compiler có thể tạo ra các member functions thuộc kiểu int. Nhưng vì phần define function( ở đây chỉ nêu mẫu constructor ) lại ở file .cpp ( ở file .h ta chỉ declare nó ), compiler không tìm thấy khi “link” các nguyên mẫu hàm ở file .h, vậy nên gây ra lỗi LNK như trên.

Có 3 cách khắc phục cho vấn đề này:
Cách 1: Định nghĩa tất cả các hàm ở trong file header:

// stack.h 

#define MAX_STACK_SIZE 100
template <typename T>;
class NStack
{
private:
    T * elem;
    int sz;
public:
    NStack()
    {
        elem = new T [MAX_STACK_SIZE];
        sz = 0;
    }
    // declaring & defining other methods
}

Làm như cách một sẽ gộp phần khai báo và định nghĩa lại làm một, không hay cho lắm, ta đi đến cách 2 và cách 3 như sau.

Cách 2: Thông báo trước cho compiler biết những kiểu dữ liệu trừu tượng nào ta muốn dùng ở cuối file cpp ( trường hợp ở đây là int )

// stack.cpp
#include "stack.h"
#define MAX_STACK_SIZE 100
template<typename T>
NStack<T>::NStack()
: elem { new T[ MAX_STACK_SIZE ] },
  sz { 0 }
{}

// defining other methods

//...

// Dùng 1 trong 2
template class NStack<int>;
NStack<int> __temp;

Phương án này cũng chưa tốt ghi mà chúng ta phải khai báo trước những kiểu dữ liệu cụ thể trước khi sử dụng, mà điều này nó lại không hợp logic lắm với ý nghĩa của template. Ta đến với cách 3 như sau.

Cách 3: Bỏ toàn bộ phần code định nghĩa hàm vào một file tạm là .tpp ( để compiler bỏ qua file này, không biên dịch, tránh xung đột khi include qua về với file header ). Sau đó include file .tpp này vào cuối file header

// stack.h 

template <typename T>
class NStack
{
private:
    T * elem;
    int sz;
public:
    NStack();

    // declaring other methods
}

#include "stack.tpp"

Happy coding 🙂

Advertisements

One thought on “Lỗi khi định nghĩa cho class template trong file .cpp và cách khắc phục

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s