user2138149
user2138149

Reputation: 16625

Should I use std::size_t to count instance of my class?

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

Answers (4)

Adrian McCarthy
Adrian McCarthy

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

eladidan
eladidan

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

phoeagon
phoeagon

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

Lightness Races in Orbit
Lightness Races in Orbit

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 ++; and instance_counter ++; inside of my copy constructor?

Yes.

Upvotes: 1

Related Questions