Joe Ganley

I make software and sometimes other things.

 

Due to Blogger's termination of support for FTP, this blog is no longer active.

It is possible that some links from here, particularly those within the site, are now broken. If you encounter one of those, your best bet is to go to the new front page and hunt for it from there.

Most, but not all, of the blog's posts are on this page; the archives are here.

 

I ran into a bug the other day that I've run into enough times in my career that I thought it was worth sharing.

One of the perils of C++ is that it does certain things behind your back, and if you don't understand what those things are, they can bite you. One way this manifests is that apparently meaning-preserving changes can have surprising consequences.

In this recent case, I inherited some code that I was cleaning up. One of the changes was to change this:

SomeClass foo;
foo = factory.getSomeClass();
into this:
SomeClass foo = factory.getSomeClass();
Simple enough, right? But after making this change, a later access to foo segfaulted.

Can you guess why? Here's a hint: SomeClass uses a reference-counted shared-data implementation, similar to std::string.

Here's what happened: SomeClass had defined an assignment operator, but not a copy constructor. The old code used the former, and the new code used the latter. In the absence of a user-defined copy constructor, the compiler generates one that just does a bitwisememberwise copy. As a result, the reference count is copied and not incremented, and so later it becomes 0 before it should, and the data is deleted. A subsequent access to that data produces the segfault.

The solution is simple: Define a correct copy constructor. My own policy is never to rely on the compiler-generated copy constructor and assignment operator. When I write a new class, if I think I don't need these, I immediately add method prototypes for them that are private and have no implementations. Then, if you ever invoke them (deliberately or not), the compile will fail, and you'll know that you have to implement them for real (or change the calling code).

Labels: ,

0 Comments: