Deallocating memory
For a project I have to implement a bitset class. My code thus far is:
Header File
#ifndef BITSET_H_
#define BITSET_H_
#include <string>
#include <cmath>
using namespace std;
// Container class to hold and manipulate bitsets
class Bitset {
public:
Bitset();
Bitset(const string);
~Bitset();
// Returns the size of the bitset
int size();
// Sets a bitset equal to the specified value
void operator= (const string);
// Accesses a specific bit from the bitset
bool operator[] (const int) const;
private:
unsigned char *bitset;
int set_size;
// Sets a bitset equal to the specified value
void assign(const string);
};
#endif /* BITSET_H_ */
Source File
#include "bitset.h"
Bitset::Bitset() {
bitset = NULL;
}
Bitset::Bitset(const string value) {
bitset = NULL;
assign(value);
}
Bitset::~Bitset() {
if (bitset != NULL) {
delete[] bitset;
}
}
int Bitset::size() {
return set_size;
}
void Bitset::operator= (const string value) {
assign(value);
}
bool Bitset::operator[] (const int index) const {
int offset;
if (index >= set_size) {
return false;
}
offset = (int) index/sizeof(unsigned char);
return (bitset[offset] >> (index - offset*sizeof(unsigned char))) & 1;
}
void Bitset::assign(const string value) {
int i开发者_如何学编程, offset;
if (bitset != NULL) {
delete[] bitset;
}
bitset = new unsigned char[(int) ceil(value.length()/sizeof(unsigned char))];
for (i = 0; i < value.length(); i++) {
offset = (int) i/sizeof(unsigned char);
if (value[i] == '1') {
bitset[offset] |= (1 << (i - offset*sizeof(unsigned char)));
} else {
bitset[offset] &= ~(1 << (i - offset*sizeof(unsigned char)));
}
}
set_size = value.length();
}
My problem is my delete statements in both the deconstructor and assign method core dump. Is it not necessary to deallocate this memory? From what I've read so far it's always necessary to use the delete command whenever you call new.
EDIT: I've changed the code above to reflect one of the fixes. I added bitset = NULL in the constructor. This fixed the core dump in the assign method however I'm still getting errors in the deconstructor.
I think you should initialize bitset
to NULL
in your second constructor.
Why?
Because a pointer variable won't necessarily be initialized to NULL
. So you may be trying to delete[]
some random memory address when you use that second constructor.
So you should have:
Bitset::Bitset(const string value) : bitset(NULL)
{
assign(value);
}
Most likely you're copying a Bitset
somewhere. You have not defined a copy constructor, not a copy assignment operator. The result of copying is then that you have two instances who both think they should deallocate the dynamically allocated array when they finish.
This is known as the Rule of Three: if you define any of destructor, copy constructor or copy assignment operator, then chances are that you'll need to define all three.
Now, about your code:
#include "bitset.h"
OK.
Bitset::Bitset() {
bitset = NULL;
}
(1) You didn't include a header that guaranteed defines NULL
.
(2) you're not initializing the member set_size
, so the check in the index operator may/will be using an indeterminate value, with Undefined Behavior.
(3) generally prefer to use initializer list rather than assignment (this avoids e.g. doing default construction followed by assignment).
Bitset::Bitset(const string value) {
bitset = NULL;
assign(value);
}
(4) Generally it's not a good idea to express construction in terms of assignment. Instead, express assignment in terms of construction.
Bitset::~Bitset() {
if (bitset != NULL) {
delete[] bitset;
}
}
(5) The check for NULL
is unnecessary; you can safely delete
a nullpointer.
int Bitset::size() {
return set_size;
}
(6) Uh, well, set_size
was the member that wasn't initialized… Also, this member function should be const
.
void Bitset::operator= (const string value) {
assign(value);
}
(7) An assignment operator should in general return a reference to the assigned-to object. That's just a convention, but it's what users of your class expect.
(8) Pass an in-argument by value or by reference to const
. Generally, for built-in types choose by-value and for other types, such as std::string
, choose reference to const
. That is, the formal argument should better be string const& value
.
bool Bitset::operator[] (const int index) const {
int offset;
if (index >= set_size) {
return false;
}
offset = (int) index/sizeof(unsigned char);
return (bitset[offset] >> (index - offset*sizeof(unsigned char))) & 1;
}
(9) First, again, the uninitialized set_size
member.
(10) Then, note that sizeof(unsigned char)
is 1 by definition. You probably want to use CHAR_BIT
from <limits.h>
here. Or just use 8 unless you plan on supporting Unisys computers (9-bit byte) or perhaps a Texas Instruments digital signal processor (16-bit byte).
void Bitset::assign(const string value) {
int i, offset;
if (bitset != NULL) {
delete[] bitset;
}
(11) The check for NULL
is unnecessary.
bitset = new unsigned char[(int) ceil(value.length()/sizeof(unsigned char))];
(12) As already mentioned, sizeof(char)
is 1 by definition.
(13) The division has integer arguments and so is an integer division, not a floating point division. Presumably what you want is the trick (a+b-1)/b
?
for (i = 0; i < value.length(); i++) {
(14) Style: declare a variable as close to its first use as practically possible. Here it means declare the loop counter i
directly in the loop head, like this: for( int i = 0, ...
.
offset = (int) i/sizeof(unsigned char);
(14) And ditto for offset
. But for this variable you're not planning on changing its value, so also declare it const
.
if (value[i] == '1') {
bitset[offset] |= (1 << (i - offset*sizeof(unsigned char)));
} else {
bitset[offset] &= ~(1 << (i - offset*sizeof(unsigned char)));
}
(15) Better rethink those shift operations!
}
set_size = value.length();
}
Cheers & hth.,
Make sure that the allocation size isn't zero, I suspect that's what's going on here, and that you're just writing to unallocated garbage memory. Running under valgrind will catch this too.
精彩评论