Reputation: 16625
I have a class:
class Nothing
{
/// Constructor, Destructor, Copy and Assignment
public:
Nothing();
~Nothing();
Nothing(const Nothing& clone);
/// Operators
const Nothing& operator=(const Nothing& other);
/// Static Members
private:
static unsigned long long id_counter;
static unsigned long long instance_counter;
};
Nothing::Nothing()
{
m_name.clear();
id_counter ++;
m_id = id_counter;
instance_counter ++;
}
Nothing::~Nothing()
{
m_name.clear();
instance_counter --;
}
Nothing::Nothing(const Nothing& other)
{
}
unsigned long long Nothing::id_counter = 0;
unsigned long long Nothing::instance_counter = 0;
Notice I am using unsigned long long to count instances of the class. Should I use std::size_t instead?
As an aside: If I have an instance of a class, and I do something like this:
Nothing instance;
instance(Nothing()); // Calling copy constructor
Will the destructor be called before the copy constructor is called? Reason for asking is do I need id_counter ++;
and instance_counter ++;
inside of my copy constructor?
Upvotes: 1
Views: 515
Reputation: 47962
std::size_t
is the type of a sizeof
expression and usually the type returned by size()
and capacity()
of the STL containers. (Technically, the STL containers each typedef a size_type
, but that's almost always equivalent to std::size_t
.)
Except for machines with weird memory models (e.g., one that distinguishes between "near" and "far" pointers), a std::size_t
will always be large enough to count all of the objects that you can fit into memory at one time. This is not a direct guarantee, but a side-effect of the way the definitions are written.
Therefore, std::size_t
is a natural type for things like instance counts.
Note that std::size_t
is an unsigned type. Many people believe you should avoid unsigned types except for representing actual bit patterns (for example, see the Google Style Guide). By using an unsigned type, some arithmetic operations may behave in surprising ways. If you had a bug that caused your instance count to go negative, it might be hard to detect, because a negative value will wrap around to a very large positive value.
Personally, I find that argument unpersuasive; signed arithmetic also behaves in surprising ways when dealing with overflow and underflow. Also, when you're trying to use STL types, assigning size()
to a signed value or comparing a signed value to size()
requires explicit casts or disabling compiler warnings. I dislike disabling compiler warnings, especially since they can help you find many of the bugs that people fear when they suggest avoiding unsigned types.
I would used std::size_t
. There's nothing wrong with unsigned long long
, but that might not scale as well with the host platform.
If you want to keep your options open, then you can add a typedef to your Nothing class, which makes it easy to change your mind later:
/// Static Members
private:
typedef unsigned long long CounterType; // consider std::size_t
static CounterType id_counter;
static CounterType instance_counter;
You could even call your typedef size_type
if you want to match the style of the STL containers.
Upvotes: 2
Reputation: 2644
std::size_t definition is implementation specific, as you can see in en.cppreference. In most implementations you will find it is defined as unsigned int
.
So to answer your question, you can use it instead of unsigned long long
if you prefer, and your code will remain portable (as long as you don't actually require it to be 64-bit long) in the sense that std::size_t is guaranteed to be defined on every c++ compiler. If the unsigned long long
requirement is a mandatory (which I doubt since you are using it for instance count) then its best to stick with unsigned long long
.
instance(Nothing); // Calling copy constructor: Should this be `instance(Nothing())`?
This is bad syntax. If you are trying to invoke the copy ctor
on a newly formed instance you should call Nothing copyInstance (Nothing());
. Of course that's a very silly thing to do since you are copying a "clean" instance, you might as well have called Nothing copyInstance;
to the same effect.
do I need id_counter ++; and instance_counter ++; inside of my copy constructor?
You should definitely increment instance_counter since you are constructing a new instance. You didn't specify what id_counter counts (reference count??) so it's hard to say.
Will the destructor be called before the copy constructor is called?
Nowhere in your code do I see a place where the dtor
will be called but since you are allocating instance
on the stack, its destructor will be called once instance
reaches the end of its scope (next }
appearance).
Upvotes: 2
Reputation: 2090
Technically, both should be okay, at least in the near future.
As is described link and link, size_t
is the type of the return value of sizeof()
operator and is guaranteed to hold the size of the largest object possible on the current platform. It's usually unsigned
also.
And yeah, using unsigned long long
is unlikely to cause any problem in the near future. Yet size_t
might gives you an extra advantage when you migrate to, say, 128bit environment, on maximum compatibility.
But if your code depends on the fact that unsigned long long
has at least 0~2^64-1 it's probably not a good idea to switch to size_t
.
yes, you need to update the counter in your copy constructor to get it straight.
P.S. I don't see why you would want to:
Nothing instance;
instance(Nothing);
maybe some member function like static Nothing Nothing::instance()
to get a new instance? // I just don't see why you would want to call a copy-ctor on a type instead of an existing instance.
And BTW you can always call printf()
and flush()
from constructors and destructors to examine their order of execution.
Upvotes: 1
Reputation: 385144
I am using unsigned long long to count instances of the class. Should I use std::size_t instead?
There's no reason to use std::size_t
and no reason not to use std::size_t
. It's a useful type for things like array indices (see: unsigned int vs. size_t), but does not correlate at all to the maximum quantity of objects that you can create in any given program. So just choose whichever you like based upon how many objects you think you're going to have.
Will the destructor be called before the copy constructor is called?
You didn't call the copy constructor at all. In fact you can never "call" any constructor. The copy constructor is automatically invoked when you create a new object and initialise it from an existing object, but nowhere in your code do you do that.
do I need
id_counter ++;
andinstance_counter ++;
inside of my copy constructor?
Yes.
Upvotes: 1