I have a templated container class, something like this toy code:
template <class ItemType> class MyVector
{
public:
MyVector() : _numItems(0), _items(NULL) {/* empty */}
/** Returns a reference to the first item in our array,
* or a default-constructed item if the array is empty.
*/
const ItemType & GetFirstItemWithDefault() const
{
return (_numItems > 0) ? _items[0] : _defaultItem;
}
[other methods omitted because they aren't relevant]
private:
int _numItems; // how many valid items (_items) points to
ItemType * _items; // demand-allocated
const ItemType _defaultItem;
};
This class is really convenient to use -- any code开发者_开发百科 can just #include "MyVector.h" and then start declaring objects of type MyVector and MyVector and so on, and it all Just Works (tm) without any futzing around required.
One thing that kind of bothers me, however, is the presence of the _defaultItem member variable, which is there solely to give GetFirstItemWithDefault() the ability to return a valid reference when the container is empty. The objection is that if I declare N MyVector objects, that means N copies of _defaultItem will be present in RAM as well --- even though they are all identical and read-only, and so there really only needs to be one of them per process, not one per MyVector.
So, the obvious solution is to make _defaultItem static .... but AFAICT that comes with a cost: if I do that, it is no longer possible for any old piece of code to simply #include "MyVector.h" and go... now the user has to be sure to declare storage for that static variable in one of his .cpp files, which is (a) a pain in the butt, and (b) means that the user of the code has to be aware of the details of the internal implementation of the class. Since _defaultItem is a private member variable, the user of the class shouldn't have to think about it or even realize it exists, let alone know that he needs to declare storage for it. (and what if two separate pieces of code both declare storage for it, each not knowing the other has done the same thing? Wouldn't that cause a duplicate-symbol linker error?)
Therefore, my question is: is there any way to tell C++ to automatically provide one unique storage (per instantiated type of MyVector) for this static member variable, so that users of MyVector don't have to know about it? (Note that it would need to be automatic for all possible instantiations of MyVector<...>, not just for a few common cases)
Why not make that default item static local to the function?
const ItemType & GetFirstItemWithDefault() const
{
static const ItemType _default;
return (_numItems > 0) ? _items[0] : _default;
}
This may not be what you want if you want to check against the default item in some other function again, but for that you can just put it in a seperate function aswell (that may be static itself):
static const ItemType& GetDefault() const
{
static const ItemType _default;
return _default;
}
And call that function when you need to access the default item.
That said, I think having a default item isn't really nice. std::vector
also doesn't have it and doesn't need it. Just tell the user to check if the vector is empty
and be done. One problem with hidden statics is, that you don't know ItemType
. It could be a class that eats tons of resources and you just made another instance of that! Maybe rethink that class' design and after that, switch to a std::vector
. :)
If it is a template, the compiler will do the magic for you. Just put the static member in the header, and the compiler will see to that it is just instantiated once.
template <class ItemType>
class MyVector
{
public:
//...
private:
static const ItemType _defaultItem;
};
template <class ItemType>
const ItemType MyVector<ItemType>::_defaultItem;
So, the obvious solution is to make _defaultItem static .... but AFAICT that comes with a cost: if I do that, it is no longer possible for any old piece of code to simply #include "MyArray.h" and go... now the user has to be sure to declare storage for that static variable in one of his .cpp files, which is (a) a pain
No. This fear is out of concern. In template
s you can (must) declare the static
variable in the same file.
template <class ItemType> class MyVector
{
const ItemType _defaultItem;
};
template <class ItemType>
const ItemType MyVector<ItemType>::_defaultItem; // ok (no multiple symbols)
Also, note that this static
member will be instantiated only if GetFirstItemWithDefault()
is called. So don't worry about redundant allocation also.
The only thing worry is to provide default constructor to all the ItemType
as the static
object will generically rely on the default constructor (which you must be caring about already, I guess). That's it.
精彩评论