1

I'm trying to implement the Singleton Pattern and Factory Pattern at the same time. In my mind there can be only one factory for each kind of factory which are based on an abstract class, and use the factory to produce the corresponding shape object.

But I got an error:

error LNK2001: unresolved external symbol "private: static class TriangleFactory * TriangleFactory::instance" (?instance@TriangleFactory@@0PAV1@A)

I don't know why this happens.Could you tell me how to fix it? The code is as follows.

#include<type_traits>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<memory>
#include<time.h>
using namespace std;

class Shape
{
public:
    virtual void display();
protected:
    int x, y;
};

class MyTriangle:public Shape
{
public:
    MyTriangle(int _x, int _y)
    {
        x = _x;
        y = _y;
    }
    MyTriangle()
    {
        x = rand() % 200;
        y = rand() % 200;
    }
    void display()
    {
        printf("%d %d\n",x,y);
    }
};

class SuperFactory
{
public:
    virtual Shape* Execute() = 0;
};

class TriangleFactory :public SuperFactory
{
public:
    Shape* Execute()
    {
        Shape *p = new MyTriangle;
        return p;
    }
    static SuperFactory* GetFactory()
    {
        return instance;
    }
private:
    TriangleFactory()
    {
        instance = nullptr;
        instance = new TriangleFactory;
    }
    ~TriangleFactory(){}
    static TriangleFactory* instance;
};

int main()
{
    MyTriangle *p = dynamic_cast<MyTriangle*>(TriangleFactory::GetFactory()->Execute());
    p->display();
    return 0;
}

would appreciate your answers :)

Jeroen Vannevel
  • 41,258
  • 21
  • 92
  • 157
AntiMoron
  • 1,118
  • 1
  • 10
  • 28
  • The code you posted shows little relationship with C++11, or at least the problem in it has nothing related. Besides, if you really are interested on how to in C++11, google is always a good helper. For singleton, possible duplicated with http://stackoverflow.com/questions/11711920/how-to-implement-multithread-safe-singleton-in-c11-without-using-mutex – Xin Huang Jan 05 '14 at 02:49

4 Answers4

4

My criticisms:

  1. You don't need all those #includes. You can get away with only <iostream>.

  2. Your constructors and deconstructors are private. I suggest that you: a.) make them protected instead, and b.) only make the constructor private. The deconstructor should be public and virtual.

  3. The function Shape::display should be declared as a pure virtual function like SuperFactory::Execute. Change it to this: virtual void display() = 0;

  4. The main problem with the other answers - simply writing TriangleFactory* TriangleFactory::instance; before your main() function is this: you'll find yourself facing a lovely null pointer error. The solution to this requires a change in your singleton. Because you initialize instance in your constructor, and no object of type TriangleFactory is ever created in the code you posted, the constructor is never called, and instance is never initialized.

My solution:

Continue with the TriangleFactory* TriangleFactory::instance; before the main() function, but set its value to null like this TriangleFactory* TriangleFactory::instance = nullptr;. Doing so avoids errors relating to past memory leaks.

Instead of initializing instance in your constructor, leave the constructor empty and change your GetFactory function to this:

static SuperFactory *GetFactory()
{
    if(!instance)
    {
        instance = new TriangleFactory();
        return instance;
    }
    else
    {   
        return instance;
    }
}

Now, why would I change all that when putting TriangleFactory *TriangleFactory::instance = new TriangleFactory(); before main() - as I suspect you did - works just fine? Well, initializing any data outside of a function is risky. The objects are created in an arbitrary order at runtime - and when you start creating classes that rely on other classes, TriangleFactory *TriangleFactory::instance = new TriangleFactory(); will blow up in your face. Don't have stuff blow up in your face. Get rid of cable and upgrade to DirecTV. No xD just kidding, but definitely don't do TriangleFactory *TriangleFactory::instance = new TriangleFactory();

Proxy
  • 1,674
  • 1
  • 14
  • 27
  • 3
    Lol - stole accepted answer. Would have gotten a hat for that yesterday :) – Proxy Jan 05 '14 at 03:44
  • A long time has been passed. And I found this solution is not correct exactly. Maybe @Proxy needs some edition. – AntiMoron Dec 14 '15 at 07:20
  • For multi-thread program, such code will cause memory leak for there being no lock for creating instance and the initialization is not atomic operation. – AntiMoron Dec 14 '15 at 07:22
  • For single thread program,the order of initializations of instance pointers out of singleton classes is not specified.It still may cause some unexpected failure. – AntiMoron Dec 14 '15 at 07:24
2

Correct way in c++ to initilize singleton in your case would be to use static instance, not static pointer. This way the object will be properly destroyed on program exit.

So the correct look of GetFactory() would be:

static SuperFactory * GetFactory(){

    static TriangleFactory factory;
    return &factory;
}

The factory instance will be created on first use of GetFactory() and destroyed on program exit. No memory leak and the most simplest solution.

UldisK
  • 1,559
  • 1
  • 15
  • 25
1

The line:

static TriangleFactory* instance; 

Declares instance. There is no definition. Add:

TriangleFactory* TriangleFactory::instance;

This will define the symbol for linker.

TimDave
  • 652
  • 3
  • 6
  • I'm new to c++.The compiler won't let "TriangleFactory::TriangleFactory * instance" pass.IDE says"1>Source.cpp(67): error C2143: syntax error : missing ';' before '*' 1>Source.cpp(67): error C2761: '{ctor}' : member function redeclaration not allowed 1>Source.cpp(67): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 1> 1>Build FAILED. 1>" – AntiMoron Jan 05 '14 at 03:03
1

You need to input below line.

TriangleFactory* TriangleFactory::instance;

The error message is telling you that the compiler cannot find the definition instance. You need to init the static member variable, instance, when you use it as class member variable.

hyun
  • 2,057
  • 2
  • 16
  • 20