Bob said:
ICInfo has a bunch of other members which I stripped out for this posting. I
simply never considered making it a class rather than a struct.
I allocate a bunch of ICInfo structures and store them in a map. Now that I
think about it, I could make ICInfo a class and the end result in my app would
be about the same.
Why not? Why can't I just say:
typedef struct ICInfo_tag {
// Use a string rather than a char*
std::string Name;
A Plain Old Struct (I should say Plain Old Data Structure, really) is
special in that it's "just like a C struct", i.e. no constructors, no
destructors, no class members. This is a fairly technical concept that's
sometimes necessary for interoperability (you can't hand over ICInfo structs
to C code if they contain a std::string). Not every struct is a PODS.
In your case, you don't need any of that. If you're not bound by such
restrictions, you should usually prefer std::string to char*, since it saves
you allocation headaches, and you should prefer "class" to "struct", since
the only difference in C++ is that the default access modifier for structs
is "public", whereas for classes it's "private". After adding the
appropriate modifier yourself there's no semantic difference, but it's
clearer for readers if you use "struct" only to emphasize that your class is
really not a class at all but a PODS.
int SomeotherMember;
// Constructor
ICInfo_tag(const char* name)
{
// Get a copy of name
this->Name = name;
}
Most people prefer this syntax:
ICInfo_tag(const char* name) : Name(name) { }
This is actually more efficient than your code because it directly
constructs Name instead of copying over a temporary. While that's usually a
micro-optimization that doesn't matter (though depending on the nature of
your objects, not always), C++ programmers obsess enough over
micro-optimization that most wouldn't be caught dead doing it the "slow"
way.
Of course, the one improvement you can and should make is to use new[] instead
of malloc().
So, instead of malloc, I would say
This->Name = new char[nameLen];
which has the advantage that it will throw an exception rather than making me
check to see if it succeeded (which my original code neglected to do)
There's more than that:
- You can mix malloc() and new[], but you have to make sure to call free()
or delete[] as appropriate (you can't mix up the pairings). Sticking with
new and new[] for everything is a lot less error-prone.
- new and new[] don't require casts.
- new and new[] can allocate objects. malloc() can only give you raw memory.
However, it's better still not to mess with explicitly allocated memory when
it's not necessary and use stack-allocated objects instead, relying on
destructors for cleanup. This is the well known RAII idiom (Resource
Acquisition Is Initialization, and conversely, destruction is resource
release). Plus, of course, std::string is a lot more convenient to work with
than char* if you have to do string manipulation.