Code Singleton kết hợp template bị lỗi

Xin chào mọi người,
Mình có một vấn đề về Singleton kết hợp với template trong C++. Mình viết nó như thế này:

Singleton.h

#pragma once

template<class T>
class Singleton
{
public:
	static T* GetInstance();
protected:
	Singleton() {}
	static T* _instance;
};

Singleton.cpp

#include "Singleton.h"

template<class T>
T* Singleton<T>::_instance = nullptr;

template<class T>
T * Singleton<T>::GetInstance()
{
	if (_instance == nullptr)
	{
		_instance = new T();
	}

	return _instance;
}

Test.h

#pragma once
#include "Singleton.h"
#include <iostream>
class Test : public Singleton<Test>
{
public:
	void Func() { std::cout << "kjklasjdla" << std::endl; }
private:
	Test() {}
	~Test(){}
};

Test.cpp

#include "Test.h"

Souce.cpp

#include "Test.h"

int main()
{
	Test::GetInstance()->Func();
}

Và VS2017 nó chửi mình thế này đây:

error LNK2019: unresolved external symbol "public: static class Test * __cdecl Singleton<class Test>::GetInstance(void)" (?GetInstance@?$Singleton@VTest@@@@SAPAVTest@@XZ) referenced in function _main
fatal error LNK1120: 1 unresolved externals

Mong mọi người giúp đỡ

Dấu ngoặc đơn để làm gì???

1 Like

@Trương Tấn Phát, Mình viết nhầm. Không có “()”

Test.h thì bạn khai báo và triển khai Func() luôn, còn trong Test.cpp lại chẳng làm gì (triển khai).

Singleton.hSingleton.cpp thì lại đúng.

Func() để cho zui thui. Mình nghĩ Error ko nằm ở đó.

C++ template ko có vụ chia file .h và .cpp. Singleton.cpp chả làm cái gì ở đây cả, gôp 2 cái Singleton.h và Singleton.cpp thành 1 cái Singleton.h

và thêm cái nữa sao T lại đi kế thừa Singleton<T>, thấy giống recursive Singleton<Singleton<Singleton<...>>> quá :V

nếu T phải khai báo private cho T() ~T() T copy ctor gì nữa thì chỉ thiếu có mỗi cái hàm getInstance để biến nó thành Singleton, thiếu 1 hàm mà đi kế thừa cả 1 class thì có phải dùng búa tạ đập ruồi ko???

thêm 1 cái nữa mất công khai báo con trỏ static T* instance làm gì, mất công phải khai báo nó = nullptr ở ngoài, mà phải truy cập các method của T thông qua -> nữa, xài thẳng reference luôn:

class Test
{
protected:
    Test() {}
    Test(const Test& test) = delete; //thiếu xóa cái này
    //~Test() {} //ko giấu cũng ko sao vì lúc này user ko gọi delete Test::getInstance() được nữa
public:
    static Test& getInstance()
    {
        static Test instance;
        return instance;
    }
    void print() { std::cout << "Test\n"; }
};

nếu Test phải có 3 dòng protected cho default ctor, copy ctor, dtor kia thì mất công copy paste 5 dòng nữa là được rồi, template chi cho khổ :V

1 Like

edit: có thể viết như sau, làm bạn với free function :V

#include <iostream>

template <class T>
T& getInstance()
{
    static T instance;
    return instance;
}
    
class Test
{
protected:
    Test() { std::cout << "Test created\n"; }
    Test(const Test&) = delete;
    friend Test& getInstance<>(); //dòng này private protected public gì cũng được
public:
    void print1() { std::cout << "Test 1\n"; }
    void print2() { std::cout << "Test 2\n"; }
};
    
class Foo
{
protected:
    Foo() { std::cout << "Foo created\n"; }
    Foo(const Foo&) = delete;
    friend Foo& getInstance<>();
public:
    void print1() { std::cout << "Foo 1\n"; }
    void print2() { std::cout << "Foo 2\n"; }
};

int main()
{
    getInstance<Test>().print1();
    getInstance<Test>().print2();
    getInstance<Test>().print2();
    getInstance<Test>().print1();
    getInstance<Foo>().print1();
    getInstance<Foo>().print2();
}

đao lòng ở chỗ ko gọi Test::g... cho nó autocomplete cái instance từ Test được mà phải gọi 1 hàm ất ơ getInstance<Test> ở đâu ra :dizzy_face:

chắc xài macro cho nó gọn đẹp quá :see_no_evil:

1 Like

@tntxtnt cảm ơn bạn nhiều nha!

Còn bạn nói đến:

Thừa kế đó thì mình cũng hk hiểu lắm! Tại mình google thấy người ta kế thừa như z. Và lúc mình còn thực tập ở 1 công ty nước ngoài, mình được đọc code của họ, họ cũng sử dụng như z. Nên muốn thử xem sau. Mình cũng hk hiểu về nó lắm. Cũng cái “style” kế thừa như z, họ dùng với Design Pattern visitor, factory, nhưng lâu quá mình quên mất tiêu ời.

Còn về:

Lúc đặt câu hỏi mình có lượt đi bớt! Để người khác đọc dễ rơi vào trọng tâm hơn. :smiley:

Và:

Mình cũng chưa nghĩ tới nó nữa. Mới thấy nó trên stackoverflow hôm qua thôi. Để thử xem sau

Tks nhiều.

1 Like

chắc là phải xài kế thừa rồi :V

class Singleton xóa cái copy ctor luôn, toán tử gán = thì chắc optional. Subclass từ Singleton chỉ cần kế thừa, làm bạn với class Singleton, và ẩn ctor đi là được

main.cpp

#include "Foo.h"

int main()
{
    Foo::instance().print();
    /*Foo anotherFoo = Foo::instance();
    anotherFoo.setData(1);
    anotherFoo.print();
    Foo::instance().print();*/
}

Singleton.h

template <class T>
class Singleton
{
protected:
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
public:
    static T& instance()
    {
        static T instance_;
        return instance_;
    }
};

Foo.h

#pragma once

#include "Singleton.h"

class Foo : public Singleton<Foo>
{
    friend class Singleton<Foo>;
private:
    Foo() = default;
public:
    void print()const;
    void setData(int);
private:
    int data = 0;
};

Foo.cpp

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

void Foo::print() const
{
    std::cout << "Foo " << data << "\n";
}

void Foo::setData(int data)
{
    this->data = data;
}

nếu ko ẩn copy ctor đi thì mấy dòng bị comment đi anotherFoo có thể tạo được, nên cần phải ẩn nó, C++11 có cú pháp = delete cho rõ ràng là ko bao giờ sử dụng copy ctor

2 Likes

Mình bị thiếu dòng:

Hèn chi nãy giờ VS nó chửi mình

error C2248: 'Test::Test': cannot access private member declared in class 'Test'

Tks lần nữa

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?