tags:

views:

167

answers:

4

Hi people,

I am fairly new to c++, especially in its techniques. My question is, how can I create a static object member of a class itself. What I mean is I declared a static member object inside a class. Example:

CFoo:CFoo *pFoo[2] = {0};

class CFoo
{
   public: static CFoo *pFoo[2];
   public: CFoo(int a);
   public: CFoo *getFoo();
};

Now the problem is, how can I create the pFoo, like I want to create two static object pFoo,

pFoo[0] = new CFoo(0);
pFoo[1] = new CFoo(1);

so that I can use the getFoo method to return one of the pFoo, like,

CFoo *getFoo()
{
   return pFoo[0]; //or pFoo(1);
}

Thanks alot guys. Hope my questions are clear.

Thanks again in advance. -sasayins

+5  A: 

You don’t need the pointers here. In fact, they’re probably not a good idea.

The following code works for me:

#include <iostream>

struct Foo {
    static Foo const foos[];

    Foo(int i) : i(i) { }

    int i;
};

Foo const Foo::foos[2] = { Foo(1), Foo(2) };

using namespace std;

int main() {
    cout << Foo::foos[0].i << endl;
}

(Notice that (to keep the demo simple) I’ve made all members public, which is probably not what you want. It works just as well with private members.)

Konrad Rudolph
+3  A: 

To initialize your array, you should write this:

CFoo* CFoo::pFoo [2] = { new CFoo(0), new CFoo(1) };

Do not free the memory allocated that way.

Pavel Shved
+1 for “Do not free the memory allocated that way.”
Konrad Rudolph
@Pavel: Warning: Your CFoo objects will not be automatically destroyed on application quit (static initialization and destruction).
moala
They won't be freed automatically, of course. But you shouldn't free them manually either.
Pavel Shved
No reason you can't free them manually, they're on the heap like any other (default) 'new'd object. They're /allocated/ before main is called, but that's the only special thing about them. Just treat them as you would any other pointer-to-heap and you'll be fine.
JDonner
+2  A: 

There are a few ways to do this. One is just to keep doing as you're doing and initialize the pFoo array like this

// In source file (not header)
CFoo* CFoo::pFoo[2] = {new CFoo(1), new CFoo(2)};

But I would suggest wrapping them in an accessor like this:

// In header
class CFoo 
{
public:
  CFoo(int a);
  CFoo* getFoo1() const;
  CFoo* getFoo2() const;

private:
  static CFoo* getFoo(int n);
};

// In source file

CFoo::CFoo(int a)
{
 // Constructor implementation
}

CFoo* CFoo::getFoo1() const
{
  return getFoo(1);
}

CFoo* CFoo::getFoo2() const
{
  return getFoo(2);
}

CFoo* CFoo::getFoo(int n)
{
  static CFoo* pFoo[2] = {new Foo(1), new Foo(2)}; 
  return pFoo[n];
}

The main reason is that this gets around the static initialization order fiasco. When you have a static variable in a class, it's somewhat nondeterministic when it is initialized, which is dangerous especially when the variable is public. Wrapping in an accessor means that the variable will be initialized when the accessor is first called, and so you'll know it is initialized before you try use it. Plus you get the benefit of lazy initialization if it is not used for a while or at all.

The code above is not thread-safe, but I doubt you're using threads here.

Also, you should probably review your coding style. Prefixing classes with C is a somewhat archaic practice, and putting "public:" before every public function is bizarre, you only need to write it once.

Tyler McHenry
+12  A: 

Let's improve your code one step at a time. I'll explain what I'm doing at each step.

Step 1, this isn't Java. You don't need to specify public for every member. Everything after public: is public until you specify something else (protected or private). I also moved the definition of pFoo after the class. You can't define a variable before it's been declared.

class CFoo
{
   public: 
      static CFoo *pFoo[2];
      CFoo(int a);
      CFoo *getFoo();
};

CFoo* CFoo::pFoo[2] = {0};

Step 2, pFoo probably shouldn't be public if you're going to have a getFoo member function. Let's enforce the interface to the class instead of exposing the internal data.

class CFoo
{
   public: 
      CFoo(int a);
      CFoo *getFoo();

   private:
      static CFoo *pFoo[2];
};

CFoo* CFoo::pFoo[2] = {0};

Step 3, you can return by pointer without bothering to use new. I've written C++ code for many years, and I'd have to look up how you delete the memory that was newed for a static member variable. It's not worth the hassle to figure it out, so let's just allocate them on the stack. Also, let's return them by const pointer to prevent users from accidentally modifying the two static CFoo objects.

class CFoo
{
   public: 
      CFoo(int a);
      const CFoo *getFoo();

   private:
      static CFoo foos[2];
};

CFoo CFoo::foos[2] = {CFoo(0), CFoo(1)};

The implementation of getFoo then becomes:

const CFoo * CFoo::getFoo()
{
   return &foos[0];  // or &foos[1]
}

IIRC, the static member foos will be allocated the first time you create a CFoo object. So, this code...

CFoo bar;
const CFoo *baz = bar.getFoo();

...is safe. The pointer named baz will point to the static member foos[0].

Kristo
You raise so many good points, I'd like to give you three votes.
sbi
+1 good advice. Also, there's really no reliable way to free the memory given to static members unless you do it explicitly. It will remain allocated until the program ends.
Tyler McHenry
nice thanks alot. but what if i want to create an object using CFoo, like for example, CFoo *pFoos[2]; pFoos[0] = new CFoo; pFoos[1] = new CFoo;then my question is, are the foos[0] and foos[1] have been allocated?
sasayins
The space to hold the pointers is allocated when you define `CFoo* CFoo::pFoos[2];` (remember this needs to go in the source file, not the header!) Since the pointers themselves are not dynamically allocated, you don't need to do anything special. The only thing you need to be aware of is that nondeterminism in static initialization order be a problem if the pointers are accessed from any other source files, which is why it is a good idea to make them private and/or use the accessor wrapper that I describe in my answer.
Tyler McHenry
@Kristo, I noticed you had the scope resolution attached to the type name rather than the variable name in a few places, so I fixed that for you.
Tyler McHenry
@sasayins, I've added a bit to my answer to hopefully answer your question.@Tyler, thank you. I didn't have a compiler handy when I wrote my answer. :-)
Kristo