new operator overloading in c++ example - c++

I have the following code which I can't understand the status after one line in main.
#include <iostream>
typedef unsigned long size_t;
const int MAX_BUFFER=3;
int buf[MAX_BUFFER]={0}; //Initialize to 0
int location=0;
struct Scalar {
int val;
Scalar(int v) : val(v) { };
void* operator new(size_t /*not used*/) {
if (location == MAX_BUFFER) {
throw std::bad_alloc();
} else {
int* ptr = &buf[location];
if ( buf[location] == 0) {
location++;
} else {
return ptr;
}
}
}
void operator delete(void* ptr) {
size_t my_loc = (int*)ptr - buf;
buf[my_loc] = location;
location = my_loc;
}
};
int main() {
Scalar *s1 = new Scalar(11);
cout << buf[0];
}
Why does the array buf in the end of this code contains the value 11 at the...?
I'm am not sure where the val input plays a role.

I don't understand why you only conditionally increment location after an allocation, and if you increment location, then the function doesn't execute a return statement which is undefined behavior.
Your deallocation strategy is completely broken unless objects are only deallocated in the exact opposite order of allocations. Additionally, after the array element has been used for one allocation, it won't ever be used again due to the assignment in the deallocator.
As for the actual question: the first Scalar allocated is allocated at the same location as the buffer, so the first Scalar and buf[0] share the same memory. As they're both composed of a single int, writes to one might be readable from the other. And when you construct the Scalar, it assigns the value 11 to the shared memory. I think doing this is undefined behavior, but I'm not certain.

The value 11 gets into the buffer thanks to
Scalar(int v) : val(v) { };
that takes the parameter and copies it into the member val.
If the class instance is allocated at the address of the buffer (because of the customized operator::new (size_t) implementation) then it's possible that its first member ends up in the first array element.
Note that this code is totally broken for several reasons already pointed out by Mooing Duck.

Related

Reallocate array with memcpy and memset

I've taken over some code, and came across a weird reallocation of an array. This is a function from within an Array class (used by the JsonValue)
void reserve( uint32_t newCapacity ) {
if ( newCapacity > length + additionalCapacity ) {
newCapacity = std::min( newCapacity, length + std::numeric_limits<decltype( additionalCapacity )>::max() );
JsonValue *newPtr = new JsonValue[newCapacity];
if ( length > 0 ) {
memcpy( newPtr, values, length * sizeof( JsonValue ) );
memset( values, 0, length * sizeof( JsonValue ) );
}
delete[] values;
values = newPtr;
additionalCapacity = uint16_t( newCapacity - length );
}
}
I get the point of this; it is just allocating a new array, and doing a copy of the memory contents from the old array into the new array, then zero-ing out the old array's contents. I also know this was done in order to prevent calling destructors, and moves.
The JsonValue is a class with functions, and some data which is stored in a union (string, array, number, etc.).
My concern is whether this is actually defined behaviour or not. I know it works, and has not had a problem since we began using it a few months ago; but if its undefined then it doesn't mean it is going to keep working.
EDIT:
JsonValue looks something like this:
struct JsonValue {
// …
~JsonValue() {
switch ( details.type ) {
case Type::Array:
case Type::Object:
array.destroy();
break;
case Type::String:
delete[] string.buffer;
break;
default: break;
}
}
private:
struct Details {
Key key = Key::Unknown;
Type type = Type::Null; // (0)
};
union {
Array array;
String string;
EmbedString embedString;
Number number;
Details details;
};
};
Where Array is a wrapper around an array of JsonValues, String is a char*, EmbedString is char[14], Number is a union of int, unsigned int, and double, Details contains the type of value it holds. All values have 16-bits of unused data at the beginning, which is used for Details. Example:
struct EmbedString {
uint16_t : 16;
char buffer[14] = { 0 };
};
Whether this code has well-defined behavior basically depends on two things: 1) is JsonValue trivially-copyable and, 2) if so, are a bunch of all-zero Bytes a valid object representation for a JsonValue.
If JsonValue is trivially-copyable, then the memcpy from one array of JsonValues to another will indeed be equivalent to copying all the elements over [basic.types]/3. If all-zeroes is a valid object representation for a JsonValue, then the memset should be ok (I believe this actually falls into a bit of a grey-area with the current wording of the standard, but I believe at least the intention would be that this is fine).
I'm not sure why you'd need to "prevent calling destructors and moves", but overwriting objects with zeroes does not prevent destructors from running. delete[] values will call the destructurs of the array members. And moving the elements of an array of trivially-copyable type should compile down to just copying over the bytes anyways.
Furthermore, I would suggest to get rid of these String and EmbedString classes and simply use std::string. At least, it would seem to me that the sole purpose of EmbedString is to manually perform small string optimization. Any std::string implementation worth its salt is already going to do exactly that under the hood. Note that std::string is not guaranteed (and will often not be) trivially-copyable. Thus, you cannot simply replace String and EmbedString with std::string while keeping the rest of this current implementation.
If you can use C++17, I would suggest to simply use std::variant instead of or at least inside this custom JsonValue implementation as that seems to be exactly what it's trying to do. If you need some common information stored in front of whatever the variant value may be, just have a suitable member holding that information in front of the member that holds the variant value rather than relying on every member of the union starting with the same couple of members (which would only be well-defined if all union members are standard-layout types that keep this information in their common initial sequence [class.mem]/23).
The sole purpose of Array would seem to be to serve as a vector that zeroes memory before deallocating it for security reasons. If this is the case, I would suggest to just use an std::vector with an allocator that zeros memory before deallocating instead. For example:
template <typename T>
struct ZeroingAllocator
{
using value_type = T;
T* allocate(std::size_t N)
{
return reinterpret_cast<T*>(new unsigned char[N * sizeof(T)]);
}
void deallocate(T* buffer, std::size_t N) noexcept
{
auto ptr = reinterpret_cast<volatile unsigned char*>(buffer);
std::fill(ptr, ptr + N, 0);
delete[] reinterpret_cast<unsigned char*>(buffer);
}
};
template <typename A, typename B>
bool operator ==(const ZeroingAllocator<A>&, const ZeroingAllocator<B>&) noexcept { return true; }
template <typename A, typename B>
bool operator !=(const ZeroingAllocator<A>&, const ZeroingAllocator<B>&) noexcept { return false; }
and then
using Array = std::vector<JsonValue, ZeroingAllocator<JsonValue>>;
Note: I fill the memory via volatile unsigned char* to prevent the compiler from optimizing away the zeroing. If you need to support overaligned types, you can replace the new[] and delete[] with direct calls to ::operator new and ::operator delete (doing this will prevent the compiler from optimizing away allocations). Pre C++17, you will have to allocate a sufficiently large buffer and then manually align the pointer, e.g., using std::align…

Allocating an array of aligned struct

I'm trying to allocate an array of struct and I want each struct to be aligned to 64 bytes.
I tried this (it's for Windows only for now), but it doesn't work (I tried with VS2012 and VS2013):
struct __declspec(align(64)) A
{
std::vector<int> v;
A()
{
assert(sizeof(A) == 64);
assert((size_t)this % 64 == 0);
}
void* operator new[] (size_t size)
{
void* ptr = _aligned_malloc(size, 64);
assert((size_t)ptr % 64 == 0);
return ptr;
}
void operator delete[] (void* p)
{
_aligned_free(p);
}
};
int main(int argc, char* argv[])
{
A* arr = new A[200];
return 0;
}
The assert ((size_t)this % 64 == 0) breaks (the modulo returns 16). It looks like it works if the struct only contains simple types though, but breaks when it contains an std container (or some other std classes).
Am I doing something wrong? Is there a way of doing this properly? (Preferably c++03 compatible, but any solution that works in VS2012 is fine).
Edit:
As hinted by Shokwav, this works:
A* arr = (A*)new std::aligned_storage<sizeof(A), 64>::type[200];
// this works too actually:
//A* arr = (A*)_aligned_malloc(sizeof(A) * 200, 64);
for (int i=0; i<200; ++i)
new (&arr[i]) A();
So it looks like it's related to the use of new[]... I'm very curious if anybody has an explanation.
I wonder why you need such a huge alignment requirement, moreover to store a dynamic heap allocated object in the struct. But you can do this:
struct __declspec(align(64)) A
{
unsigned char ___padding[64 - sizeof(std::vector<int>)];
std::vector<int> v;
void* operator new[] (size_t size)
{
// Make sure the buffer will fit even in the worst case
unsigned char* ptr = (unsigned char*)malloc(size + 63);
// Find out the next aligned position in the buffer
unsigned char* endptr = (unsigned char*)(((intptr_t)ptr + 63) & ~63ULL);
// Also store the misalignment in the first padding of the structure
unsigned char misalign = (unsigned char)(endptr - ptr);
*endptr = misalign;
return endptr;
}
void operator delete[] (void* p)
{
unsigned char * ptr = (unsigned char*)p;
// It's required to call back with the original pointer, so subtract the misalignment offset
ptr -= *ptr;
free(ptr);
}
};
int main()
{
A * a = new A[2];
printf("%p - %p = %d\n", &a[1], &a[0], int((char*)&a[1] - (char*)&a[0]));
return 0;
}
I did not have your align_malloc and free function, so the implementation I'm providing is doing this:
It allocates larger to make sure it will fit in 64-bytes boundaries
It computes the offset from the allocation to the closest 64-bytes boundary
It stores the "offset" in the padding of the first structure (else I would have required a larger allocation space each time)
This is used to compute back the original pointer to the free()
Outputs:
0x7fff57b1ca40 - 0x7fff57b1ca00 = 64
Warning: If there is no padding in your structure, then the scheme above will corrupt data, since I'll be storing the misalignement offset in a place that'll be overwritten by the constructor of the internal members.
Remember that when you do "new X[n]", "n" has to be stored "somewhere" so when calling delete[], "n" calls to the destructors will be done. Usually, it's stored before the returned memory buffer (new will likely allocate the required size + 4 for storing the number of elements). The scheme here avoid this.
Another warning: Because C++ calls this operator with some additional padding included in the size for storing the array's number of elements, you'll might still get a "shift" in the returned pointer address for your objects. You might need to account for it. This is what the std::align does, it takes the extra space, compute the alignment like I did and return the aligned pointer. However, you can not get both done in the new[] overload, because of the "count storage" shift that happens after returning from new(). However, you can figure out the "count storage" space once by a single allocation, and adjust the offset accordingly in the new[] implementation.

Portable emulation of flexible array member in C++?

I'm writing a skip list.
What I have:
template<typename T>
struct SkipListNode
{
T data;
SkipListNode* next[32];
};
The problem with this code is that it wastes space - it requires all nodes to contain 32 pointers. Especially considering that in typical list, half of the nodes will only need one pointer.
The C language has a neat feature called flexible array member that could solve that problem. Had it existed in C++ (even for trivial classes), I could write code like this:
template<typename T>
struct SkipListNode
{
alignas(T) char buffer[sizeof(T)];
SkipListNode* next[];
};
and then manually create nodes with a factory function and destroying them when deleting elements.
Which brings the question - how can I emulate such functionality portably, without undefined behaviour in C++?
I considered mallocing the buffer and then manipulating the offsets appropriately by hand - but it's too easy to violate the alignment requirements - if you malloc(sizeof(char) + sizeof(void*)*5), the pointers are unaligned. Also, I'm not even sure if such hand-created buffers are portable to C++.
Note that I don't require the exact syntax, or even ease of use - this is a node class, internal to the skip list class, which won't be a part of the interface at all.
This is the implementation I wrote, based on R. Martinho Fernandes's idea - it constructs a buffer that happens to have a correct size and alignment in specific places (the AlignmentExtractor is used extract the offset of the pointer array, which ensures that the pointers in the buffer have correct alignment). Then, placement-new is used to construct the type in the buffer.
T isn't used directly in AlignmentExtractor because offsetof requires standard layout type.
#include <cstdlib>
#include <cstddef>
#include <utility>
template<typename T>
struct ErasedNodePointer
{
void* ptr;
};
void* allocate(std::size_t size)
{
return ::operator new(size);
}
void deallocate(void* ptr)
{
return ::operator delete(ptr);
}
template<typename T>
struct AlignmentExtractor
{
static_assert(alignof(T) <= alignof(std::max_align_t), "extended alignment types not supported");
alignas(T) char data[sizeof(T)];
ErasedNodePointer<T> next[1];
};
template<typename T>
T& get_data(ErasedNodePointer<T> node)
{
return *reinterpret_cast<T*>(node.ptr);
}
template<typename T>
void destroy_node(ErasedNodePointer<T> node)
{
get_data(node).~T();
deallocate(node.ptr);
}
template<typename T>
ErasedNodePointer<T>& get_pointer(ErasedNodePointer<T> node, int pos)
{
auto next = reinterpret_cast<ErasedNodePointer<T>*>(reinterpret_cast<char*>(node.ptr) + offsetof(AlignmentExtractor<T>, next));
next += pos;
return *next;
}
template<typename T, typename... Args>
ErasedNodePointer<T> create_node(std::size_t height, Args&& ...args)
{
ErasedNodePointer<T> p = { nullptr };
try
{
p.ptr = allocate(sizeof(AlignmentExtractor<T>) + sizeof(ErasedNodePointer<T>)*(height-1));
::new (p.ptr) T(std::forward<T>(args)...);
for(std::size_t i = 0; i < height; ++i)
get_pointer(p, i).ptr = nullptr;
return p;
}
catch(...)
{
deallocate(p.ptr);
throw;
}
}
#include <iostream>
#include <string>
int main()
{
auto p = create_node<std::string>(5, "Hello world");
auto q = create_node<std::string>(2, "A");
auto r = create_node<std::string>(2, "B");
auto s = create_node<std::string>(1, "C");
get_pointer(p, 0) = q;
get_pointer(p, 1) = r;
get_pointer(r, 0) = s;
std::cout << get_data(p) << "\n";
std::cout << get_data(get_pointer(p, 0)) << "\n";
std::cout << get_data(get_pointer(p, 1)) << "\n";
std::cout << get_data(get_pointer(get_pointer(p, 1), 0)) << "\n";
destroy_node(s);
destroy_node(r);
destroy_node(q);
destroy_node(p);
}
Output:
Hello world
A
B
C
Longer explanation:
The point of this code is to create a node dynamically, without using types directly (type erasure). This node stores an object, and N pointers, with N variable at runtime.
You can use any memory as if it had a specific type, provided that:
size is correct
alignment is correct
(only non-triviably constructible types) you manually call the constructor before using
(only non-triviably destructible types) you manually call the destructor after using
In fact, you rely on this every time you call malloc:
// 1. Allocating a block
int* p = (int*)malloc(5 * sizeof *p);
p[2] = 42;
free(p);
Here, we treat the chunk of memory returned by malloc as if it was an array of ints. This must work because of these guarantees:
malloc returns a pointer guaranteed to be properly aligned for any object type.
If your pointer p points to aligned memory, (int*)((char*)p + sizeof(int)) (or p + 1, which is equivalent) also does.
The dynamically created node must have enough size to contain N ErasedNodePointers (which are used as handles here) and one object of size T. This is satisfied by allocating enough memory in create_node function - it will allocate sizeof(T) + sizeof(ErasedNodePointer<T>)*N bytes or more, but not less.
That was the first step. The second is now we extract the required position relative to the beginning of a block. That's where AlignmentExtractor<T> comes in.
AlignmentExtractor<T> is a dummy struct I use to ensure correct alignment:
// 2. Finding position
AlignmentExtractor<T>* p = (AlignmentExtractor<T>*)malloc(sizeof *p);
p->next[0].ptr = nullptr;
// or
void* q = (char*)p + offsetof(AlignmentExtractor<T>, next);
(ErasedTypePointer<T>*)q->ptr = nullptr;
It doesn't matter how I got the position of the pointer, as long as I obey the rules of pointer arithmetic.
The assumptions here are:
I can cast any pointer to void* and back.
I can cast any pointer to char* and back.
I can operate on a struct as if it was a char array of size equal to the size of the struct.
I can use pointer arithmetic to point at any element of an array.
These all are guaranteed by C++ standard.
Now, after I have allocated the block of enough size, I calculate the offset with offsetof(AlignmentExtractor<T>, next) and add it to the pointer pointing to the block. We "pretend" (the same way the code "1. Allocating a block" pretends it has an array of ints) the result pointer points to beginning of the array. This pointer is aligned correctly, because otherwise the code "2. Finding position" couldn't access the next array due to misaligned access.
If you have a struct of standard layout type, the pointer to the struct has the same address as the first member of the struct. AlignmentExtractor<T> is standard layout.
That's not all though - requirements 1. and 2. are satisfied, but we need to satisfy requirements 3. and 4. - the data in the node doesn't have to be trivially constructible or destructible. That's why we use placement-new to construct the data - the create_node uses variadic templates and perfect forwarding to forward arguments to the constructor. And the data is destroyed in the destroy_node function by calling the destructor.

How to declare n-th order pointers in runtime in C++

Pointers can be declared like this:
int
a = 1,
*b = &a, // 1st order pointer
**c = &b, // 2nd order pointer
***d = &c, // 3rd order pointer
****e = &d, // 4th order pointer
*****f = &e, // 5th order pointer
*** n-stars *** f; // n-th order pointer
Here, we need to know at compile-time the order of the pointer when we are declaring it.
Is it possible at all to declare a pointer, whose order is only known at run time? Linked to this question is whether is it possible to query at run-time the order of an arbitrary pointer?
int order = GET_ORDER_OF_PTR(f) // returns 5
int /* insert some syntax here to make ptr a pointer of order (order + 1) */ ptr = &f;
Note:
I already know this (generally) might not be a good idea. Still want to know if it's doable :)
In runtime you cannot - because C++ is statically typed. During compilation it is possible with templates, e.g.
template<typename T, int order> struct P
{
typedef typename P<T, order-1>::pointer* pointer;
};
template<typename T> struct P<T, 0>
{
typedef T pointer;
};
Then P<int, 3>::pointer is equivalent to int***.
You can't. C++ is statically typed, so the order of a pointer (which is part of its type) must be known at compile time.
Not in C or C++ because they're statically typed languages (i.e. the type of the values you store in a variable are known at compile time and fixed).
You can emulate this kind of possibility by defining a C++ class
template<typename T>
struct NPtr {
int order;
void *p; // order 0 -> T*, otherwise NPtr<T>*
NPtr(NPtr *ptr) : islast(ptr->order+1), p(ptr) {}
NPtr(T *final) : islast(0), ptr(final) {}
NPtr<T>& nptr() {
assert(order > 0);
return *(NPtr<T>*)p;
}
T& final() {
assert(order == 0);
return *(T*)p;
}
};
A TPtr<int> instance can either be a pointer to an integer (when order=0) or a pointer to another TPtr<int> instance.
The semantic equivalent of what you want is a linked list, with a number of node determined at runtime:
#include <iostream>
union kind_of_pointer
{
int data;
kind_of_pointer *next;
kind_of_pointer(int val) : data(val) {}
kind_of_pointer(kind_of_pointer* ptr) : next(ptr) {}
operator int()
{
return data;
}
kind_of_pointer& operator *()
{
return *next;
}
};
int main(void)
{
kind_of_pointer dyn_ptr{new kind_of_pointer{new kind_of_pointer{new kind_of_pointer{42}}}};
int*** static_ptr = new int**{new int *{new int{42}}};
std::cout << ***dyn_ptr << std::endl;
std::cout << ***static_ptr << std::endl;
}
I find this funny, interesting and horrible :)
You can do this:
int *f = nullptr;
decltype(f) *newptr = 0;
As for the
whether is it possible to query at run-time the order of an arbitrary
pointer?
part: no, you most definitely can not, unless you write your own wrapper-class that stores the order of the pointer. Pointer is, basically, a number: the address in memory.
This gives you at least one problem: you can't follow the pointer "all the way through" (which you would have to do to check if the thing your pointer is pointing at is itself a pointer) without potentially causing a segfault or reading "not your application's memory" (which is often prohibited by OS and will cause your application to be aborted; not sure about weather you can prevent this or not).
For example, NULL (or 0) can be cast to any pointer type, so is itself a pointer. But does it point to another pointer? Let's find out... BAM! SEGFAULT.
Oh, wait, there's another problem: you can cast (with c-style cast or with reinterpret_cast) pointer of any type to pointer of any other type. So, say, a might be pointing to a pointer, but was cast to a different type. Or a might have a type of "pointing to a pointer", but actually isn't pointing to one.
P.S. Sorry for using the verb "point" so freely.
... we need to know at compile-time the order of the pointer when we are declaring it.
Yes it is possible at compile time to determine the order of the pointer type being declared; based on the type (possibly via typedef) or the variable, if C++11 can be used (via decltype()).
#include <iostream>
using namespace std;
template <typename P>
struct ptr_order
{
static const int order = 0;
};
template <typename P>
struct ptr_order<P*>
{
static const int order = ptr_order<P>::order + 1;
};
int main()
{
typedef int*** pointer;
cout << ptr_order<pointer>::order << endl; // outputs 3
// could also use decltype if available...
// int*** p;
// ptr_order<decltype(p)>::order is also 3
}
The runtime calculation of the order of the pointer is not possible, since C++ is statically typed.

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.