tags:

views:

195

answers:

8

I need class, which cann't have more than N instances. How can i make it?

A: 

Use a fixed sized array?

DanDan
+5  A: 

First of all, why?

Once we get that question out of the way, you already know how to make a class that allows for just one instance, I assume? Usually, you do that with a static instance and a private constructor (or some variation thereof). To generalise that to N instances, all you need to do is store a static array of instances.

Finally, seriously, WHY?

Dean Harding
The answer to your `WHY?` question is simple: for the exact same reasons you would want a class with only one instance.
soulmerge
consider that most of the time when some one 'wants' a singleton, they don't really.
thecoshman
@soulmerge: Absolutely not. A C++ `Singleton` can serve as a replacement for a global object whose constructor might throw, for instance. It's behavior is simple. An `N-ton`'s behavior isn't that trivial or simple. Should the first N calls to `new Nton()` construct a new `Nton`, and the following calls return the last one? or Should there be an object pool from which `Nton`s are taken? Or should there be a special constructor for creating a new `Nton` that can be called no more than N times, and a regular constructor that gives you one of the existing `Nton`s?
conio
@conio: You are listing implementation details, but the *motivation* for such a construct (called `Nton` here) is the still the same as for the singleton. Nonetheless, I immediately thought of a factory that returns objects from a pool: isn't the singleton exactly such a construct (returning objects from a pool of size 1)?
soulmerge
@soulmerge: I disagree. The motivation for a singleton is to replace a single global, to establish a one true object. I don't see how this generalizes to N. Can you give a reason for an Ngleton without mentioning a singleton at all? (BTW, returning objects from a pool of size 1 is an implementation detail, not a design pattern.)
David Thornley
@David Thornley: I thought adding the word 'nonetheless' to my comment would clarify that I am aware of the fact that this is an implementation detail (guess I would have needed a stronger word here). But back to topic: I don't think a singleton is intended to be a global (I now many, *many* people use it that way, but that's not what it was invented for). A singleton is there to make sure you never have more than one object of that class. Now replace one with two in the previous sentence.
soulmerge
@soulmerge: A singleton is there to make sure you have one of an object, not "never more than one".
David Thornley
@David Thornley: Ok, take that definition if you want to (although I know many singleton implementations that create the single instance on demand, having a null pointer up to that point, i.e. *no* object) and replace the word 'one' with 'two'.
soulmerge
+4  A: 

Use a static variable that is called count for example. In the constructor just add one each time an object is created:

struct myclass
{
  myclass()
  {
    if(count == N) /*throw some exception!*/
    ++count;
  }
  ~myclass()
  {
    --count;
  }
private:
  static std::size_t count = 0;
};
AraK
Don't forget that you will also need to have deconstructor reduce this count so that you can have more then N instances over the life of the program, but just never more then N at any one time.
thecoshman
This answer is probably better than mine, depending on what the expected usage pattern is, I guess...
Dean Harding
@codeka I don't think so. The "Why" is more important than "How" :)
AraK
@codeka I disagree - besides the fact that the "why" is more important than "how", it's also worth noting that the "singleton" pattern doesn't involve having at most one instance, but typically it's about having EXACTLY one (i.e., it doesn't get destroyed if nobody uses it). The "Ngleton" pattern should do the same, so IMO your answer is better even from the "how" p.o.v. (but of course, mine is even better :P )
Virgil
A: 

Use a factory method for instance creation. Also have a static variable in the class to to keep track of number of instances in existence. If the upper limit is hit you can trow an exception or return an already created object, depends on your design. To enable object creation only through the factory method you'll have to make the ctor private.

codaddict
A: 

Unless it 100% makes sense for that class to limit itself to N instances, you should really wrap the class into a static access class that will hardly limiting the numbers of instances.

thecoshman
+1  A: 

The only purpose I can see for something like this is to create a pool of "workers" (or something similar). The best way to achieve this is to create two classes: one "WorkerPool" class and one "Worker" class. Something like this:

typedef boost::shared_ptr<Worker> WorkerPtr;

class Worker{
   // do the worker stuff here
}

class WorkerPool{
   public:
     const int MAX_WORKERS = 20;
     WorkerPool(){ for(int i=0;i<MAX_WORKERS;i++)
                        _pool.push_back(WorkerPtr(new Worker()) );
                  }

     WorkerPtr get(){
          if(_pool.size()>0) return _pool.pop_front();
          return WorkerPtr();
     }    

     void release(WorkerPtr w){
          return _pool.push_back(w);
     }    

     private:
       std::list<WorkerPtr> _pool;

}

It's just an example, you may do it differently (including making the WorkerPool a singleton, synchronizing get() instead of returning NULL, etc.) - but you get the picture.

Virgil
There are a number of issues here... Why do you always return the first item of the list ? How could the `_pool` be empty since you initialize it in the constructor ? You should think about it some more. Also, the objects might not be default constructible.
Matthieu M.
Actually, yeah, this probably is the best answer - if you were going to do this "at all". @Matthieu: `pop_front()` removes the first element from the list and returns it. You typically return the most recent element you put back on the list (i.e. the first one) because it gives you better cache coherency (most recent worker will have most of it's "stuff" still in the cache).
Dean Harding
Well, actually I push in the back of the list, and pop from the front, thus rotating the workers; but it would probably make sense, as you say, to push in the front of the list... oh well, details.@Matthieu, you are completely missing the point, Worker is not even a real class... this example just illustrates an idea. What exactly prevents you from instantiating the worker with some parameters, if needed? Also, of course the example can be potentially complicated to no end (construct the workers on demand, recycle them if reused too many times, etc ). But these are all implementation details.
Virgil
You still need to access your front-element before popping. But I would prefer to call this a pool of resources over an Nton, multiton, manyton...
stefaanv
`@Virgil`, `@codeka` sorry. Since `return list.pop_front()` is non-sensical I had assumed you meant `return list.front()`. Note that for exception safety reasons the `pop` operation cannot return anything (because the copy constructor could throw). You could of course use `front()` to copy the item into a temporary, use `pop_front()` to remove it from the list and return the temporary. After all it's unlikely that `shared_ptr` copy constructor would throw :)
Matthieu M.
A: 
Vera
Please do not add additions as Answer, just edit your original post. This is not a Forum.
dbemerlin
Moreover, this doesn't resemble a Singleton design pattern much. A singleton is the one definitive object, not a limit on the number of resources.
David Thornley
+1  A: 

First, a compiling and working version of the code "public" is:

static Singleton*  Instance() {
  static Singleton *p;
  if(!p) { p = new Singleton; }
  return p;
}

For the sake of the exercise, you could modify to N instances by using

static Singleton* Instance(int n) {
  static Singleton *p[N];
  if(!p[n]) { p[n] = new Singleton; }
  return p[n];
}

Error checking omitted.

Peter G.