Generic class for dynamic arrays in c++ - c++

Is it possible to make a generic class that acts as a dynamic array of objects of any type?
I want a class that basically does this:
MyObj * obj1 = new MyObj();
MutableArray * arr = new MutableArray();
arr->addObject(obj1);
MyObj * obj2 = arr->objectAtIndex(0);
// obj1 and obj2 now points to the same object
This is what the code would look like. I know this doesn't work, but you get the idea.
What I need is some generic type for an object. The array itself just consists of pointers so the size of the object shouldn't matter, right?
So, is this possible?
.h-file
class MutableArray
{
private:
class * objs;
int length;
int capacity;
public:
MutableArray();
void add(class * obj);
class objectAtIndex(int index);
};
cpp-file
MutableArray::MutableArray()
{
length = 0;
capacity = 0;
}
void MutableArray::add(class * obj)
{
if(length >= capacity)
{
this->enlarge();
}
objs[length] = obj;
length++;
}
void MutableArray::enlarge()
{
int newCapacity = (capacity * 2)+1;
class * newObjs = new class[newCapacity]
if(capacity != 0)
{
delete [] objs;
}
objs = newObjs;
capacity = newCapacity;
}
class MutableArray::objectAtIndex(int index)
{
return objs[index];
}

This has already been invented and is called std::vector<>.

Is it possible to make a generic class that acts as a dynamic array of objects of any type?
For the dynamic array use std::vector, for objects of any type use boost::any.
std::vector< boost::any > anything;

You want to use STL std::vector.

Yes. It actually exists and is called std::vector.

Related

C API: Error allocating / deallocating memory for array

I'm in the process of implementing an API for C. The code base itself is purely written in C++ and I only plan to offer said interface for any consumer using C. The interface is defined in a .h file, whereas the implementation itself is written in C++. I've read multiple times that using C++ to implement a C interface is not the best idea, but it works great in my case.
Anyway the header definition looks similar to this:
extern 'C' {
typedef struct Person {
const char *name;
uint32_t age;
uint32_t post_code;
} Person;
typedef struct PersonArray {
Person *person;
size_t size;
} PersonArray;
PersonArray *create(size_t size);
void destroy(PersonArray *array);
int fillArray(PersonArray *array);
}
I'd like the consumer to retrieve a handle for PersonArray, which contains an array of Person structure, allocated with the size passed to the create() function.
Since the implementation is in C++ I've tried to allocate the memory the following way:
static inline Person convert(const otherNamespace::Person &o_person) {
Person p{};
p.name = o_person.name;
p.age = o_person.age;
p.post_code = o_person.post_code;
return p;
}
PersonArray *create(size_t size) {
if (size <= 0) {
return nullptr;
}
PersonArray *array = new PersonArray();
array->size = size;
array->person = new Person[size]
return array;
}
void destory(PersonArray *array) {
delete array;
}
int fillArray(PersonArray *array) {
if (array == nullptr) {
return 1;
}
auto data = // retrieve std::vector<otherNamespace::Person> via RPC
for (auto i{0U}; i < array->size; i++) {
array->person[i] = convert(data.at(i);
}
return 0;
}
Unfortunately, this approach does not seem to work correctly, because when using a memchecker like valgrind, there are still blocks on the heap that are not correctly deallocated. I suppose the line new Person[size] does not get deallocated.
Any idea how to fix this memory leak? Or is there another design which would be better suited for this specific use case? If possible, I would really like to keep the implementation in C++.
You must use delete on person before array, but since it was allocated with new [] you must delete it with delete [].
void destroy(PersonArray *array) {
if (array) {
if (array->person) {
delete [] array->person;
}
delete array;
}
}

How to use shared_ptr to manage an object placed with placement new?

A fairly common thing I need to do is allot an object and some memory it'd like, in a strictly contagious region of memory together:
class Thing{
static_assert(alignof(Thing) == alignof(uint32), "party's over");
public:
~Thing(){
//// if only, but this would result in the equivalent of `free(danglingPtr)` being called
//// as the second stage of shared_ptr calling `delete this->get()`, which can't be skipped I believe?
// delete [] (char*)this;
}
static Thing * create(uint32 count) {
uint32 size = sizeof(Thing) + sizeof(uint32) * count; // no alignment concerns
char * data = new char[size];
return new (data)Thing(count);
}
static void destroy(Thing *& p) {
delete [] (char*)p;
p = NULL;
}
uint32 & operator[](uint32 index) {
assert(index < m_count);
return ((uint32*)((char*)(this + sizeof(Thing))))[index];
}
private:
Thing(uint32 count) : m_count(count) {};
uint32 m_count;
};
int main(){
{
auto p = shared_ptr<Thing>(Thing::create(1));
// how can I tell p how to kill the Thing?
}
return 0;
}
In Thing::Create() this is done with placement new into a section of memory.
I'd also like to have a shared pointer manage it in this case, using auto p = shared_ptr<Thing>(Thing::create(1)). But If it calls the equivalent of delete p.get() when the ref count empties, that'd be undefined as it mismatches the type and, more importantly, mismatches plural new with singular delete. I need it to delete in a special way.
Is there a way to easily set that up without defining an outside function? Perhaps by having the shared pointer call Thing::destroy() when the ref count empties? I know that shared pointer can accept a "deleter" as a template argument, but I'm unsure how to use it, or if it's even the proper way to address this?
std::shared_ptr accepts a deleter function as a second parameter, so you can use that to define how the managed object will be destroyed.
Here's a simplified example:
class Thing
{
public:
~Thing()
{
std::cout << "~Thing\n";
}
static std::shared_ptr<Thing> create() {
char * data = new char[sizeof(Thing)];
Thing* thing = new (data) Thing{};
return std::shared_ptr<Thing>{thing, &Thing::destroy};
}
static void destroy(Thing* p) {
p->~Thing();
delete [] (char*)p;
}
};
int main()
{
auto p = Thing::create();
}
Live Demo

The ref_count of this smart pointer needs to be heap allocated?

I was taking a look at the smart pointer implementation from a book (cracking the code interview) which reads like the following:
#include<iostream>
using namespace std;
template <class T>
class SmartPointer
{
protected:
T* ref;
unsigned int* ref_count;
public:
SmartPointer(T* ptr)
{
ref = ptr;
ref_count = (unsigned*)malloc(sizeof(unsigned));
*ref_count = 1;
}
SmartPointer(SmartPointer<T>& sptr)
{
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
SmartPointer<T> & operator=(SmartPointer<T>& sptr)
{
if (this != &sptr)
{
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
return *this;
}
~SmartPointer()
{
--*ref_count;
if (!ref_count)
{
delete ref;
free(ref_count);
ref = NULL;
ref_count = NULL;
}
}
};
class Car
{
};
int main()
{
SmartPointer<Car> car = new Car;
}
Since this came from a section about C++:
Question 1) Wouldn't it make more sense to have the following?
ref_count = new unsigned int ; // instead of ref_count = (unsigned*)malloc(sizeof(unsigned));
delete ref_count; // instead of free(ref_count);
Question 2) Or to have ref_count on the stack like in the following?
#include<iostream>
using namespace std;
template <class T>
class SmartPointer
{
protected:
T* ref;
unsigned int ref_count;
public:
SmartPointer(T* ptr)
{
ref = ptr;
// ref_count = new unsigned int ; // ref_count = (unsigned*)malloc(sizeof(unsigned));
ref_count = 1;
}
SmartPointer(SmartPointer<T>& sptr)
{
ref = sptr.ref;
ref_count = sptr.ref_count;
++ref_count;
}
SmartPointer<T> & operator=(SmartPointer<T>& sptr)
{
if (this != &sptr)
{
ref = sptr.ref;
ref_count = sptr.ref_count;
++ref_count;
}
return *this;
}
~SmartPointer()
{
--ref_count;
if (!ref_count)
{
delete ref;
// delete ref_count; // free(ref_count);
ref = NULL;
// ref_count = NULL;
}
}
};
class Car
{
};
int main()
{
SmartPointer<Car> car = new Car;
}
Question 3) There are many types of smart pointers (https://en.cppreference.com/book/intro/smart_pointers), implementation above looks like a unique_prt, shared_ptr or perhaps a weak_ptr?
Appreciate if you recommend a smart pointer implementation I should take a look.
Question 1) Wouldn't it make more sense to [use new instead of malloc]?
Sure. Only reason to need malloc in C++ is to use a poorly designed C API that requires it. There is no need to use it here.
Question 2) Or to have ref_count on the stack like in the following?
Think about how destruction of one pointer could affect all other refcounts stored somewhere else. It would not work.
Question 1) Wouldn't it make more sense to have the following?
Since unsigned is default initialized (in short, no initialization action at all!), so by using new/delete you're just using another pair of allocator/deallocator. Of course this would make your code more C++.
Question 2) Or to have ref_count on the stack like in the following?
Stack-like allocating reference counter is not possible. You may give it a try, and you will figure out why not possible, on yourself.

Qt QList - removeAll deallocating memory

I have a QList of MyClass pointers like this:
QList<MyClass*> myList;
When I call myList.removeAll(someObjPtr); it doesn't just remove the pointers from the QList, it calls delete on them internally. Is there a way to get around this or is there an alternative QList method that would just remove the elements without deallocating them?
EDIT: Internals of removeAll:
template <typename T>
Q_OUTOFLINE_TEMPLATE int QList<T>::removeAll(const T &_t)
{
int index = indexOf(_t);
if (index == -1)
return 0;
const T t = _t;
detach();
Node *i = reinterpret_cast<Node *>(p.at(index));
Node *e = reinterpret_cast<Node *>(p.end());
Node *n = i;
node_destruct(i);
while (++i != e) {
if (i->t() == t)
node_destruct(i);
else
*n++ = *i;
}
int removedCount = e - n;
d->end -= removedCount;
return removedCount;
}
As you can see it calls node_destruct, which does this:
template <typename T>
Q_INLINE_TEMPLATE void QList<T>::node_destruct(Node *n)
{
if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) delete reinterpret_cast<T*>(n->v);
else if (QTypeInfo<T>::isComplex) reinterpret_cast<T*>(n)->~T();
}
As you see, there is a delete being called.
QList has nothing to do with Qt 3's QPtrList.
None of the QList methods interpret stored pointers in a special way. You must be testing it wrongly. For example, the code below happily leaks two C instances and never deletes them.
Perhaps you were thinking of qDeleteAll from QtAlgorithms? This one will delete the instances.
Note that QList's implementation may allocate per-item memory to store your instances in, and will free that memory when appropriate. In no case will QList implementation delete a pointer that you store in the list: the only way QList interprets the stored data is via its type, and then it's only to decide whether the items are memory-movable, and whether they fit into a void* or do they need to be individually allocated. In fact, all pointer types are stored in QList as if it were a QVector, with a bit of room added at the beginning and the end to make push_front and push_back have amortized O(1) cost.
#include <QtCore>
struct C {
static int ctr;
C() { ctr ++; }
~C() { ctr --; qDebug() << (void*)this << "C instance destructed"; }
};
int C::ctr;
int main() {
auto c1 = new C, c2 = new C;
auto list1 = QList<C*>() << c1 << c2;
list1.removeAll(c1); // doesn't delete the pointed-to objects
list1.removeAll(c2);
Q_ASSERT(list1.isEmpty());
Q_ASSERT(C::ctr == 2);
// we'll happily leak both instances above
auto list2 = QList<C*>() << new C << new C << new C;
qDeleteAll(list2); // invokes delete on all objects
Q_ASSERT(C::ctr == 2);
}
node_destruct does not delete the objects pointed to by QLists of pointers. If you look closely, it only deletes n->v for large and static types. QList internally allocates large and static types on the heap, so the delete is necessary.
For pointer types, the QTypeInfo<T> specialization is:
template <typename T>
class QTypeInfo<T*>
{
public:
enum {
isPointer = true,
isComplex = false,
isStatic = false,
isLarge = false,
isDummy = false
};
};
As you see, pointers are neither large nor static, so are not deleted

Pushing local objects into a list

I have a class
class Invader
{
public:
Invader();
~Invader();
public:
void Init(InvaderTypes invadertype, CIw2DImage *AlienImage);
void Update(float dt);
void Render();
void SetAlienImage(CIw2DImage *image){ ImageAlien = image; }
void setVisible(bool show) { Visible = show; }
bool isVisible() const { return Visible; }
Iw2DSceneGraph::CSprite *AlienSprite;
Iw2DSceneGraph::CAtlas *AlienAtals;
CIw2DImage *ImageAlien;
std::list<Bullet*> *Bullets;
CIwFMat2D Transform; // Transform matrix
bool Visible; // Sprites visible state
bool Canfire;
};
void Invader::Init(InvaderTypes invadertype, CIw2DImage *AlienImage)
{
if (invadertype == InvaderTypes::TOP_ALIEN)
{
//SetAlienImage(AlienImage);
mImageAlien = AlienImage;
// Create EnemyTop atlas
int frame_w = (int)(mImageAlien->GetWidth() / 2);
int frame_h = (int)(mImageAlien->GetHeight());
AlienAtals = new CAtlas(frame_w, frame_h, 2, mImageAlien);
AlienSprite = new CSprite();
AlienSprite->m_X = 0;
AlienSprite->m_Y = 0;
AlienSprite->SetAtlas(AlienAtals);
AlienSprite->m_W = (float)AlienAtals->GetFrameWidth();
AlienSprite->m_H = (float)AlienAtals->GetFrameHeight();
AlienSprite->m_AnchorX = 0.5;
AlienSprite->SetAnimDuration(2);
}
else if (invadertype == InvaderTypes::MIDDLE_ALIEN)
{
}
else if (invadertype == InvaderTypes::LAST_ALIEN)
{
}
Visible = true;
Bullets = new std::list<Bullet*>();
Canfire = true;
}
Invader::Invader()
{
}
Invader::Invader(const Invader&other)
{
AlienAtals = new CAtlas();
AlienSprite = new CSprite();
*AlienAtals = *other.AlienAtals;
*AlienSprite = *other.AlienSprite;
}
I try to initialize it by:
list<Invader> *invaders = new list<Invader>();
int spacing = 10;
for (int i = 0; i < 5; i++)
{
Invader invader;
invader.Init(TOP_ALIEN, gameResources->getAlienImageTop());
invader.AlienSprite->m_X = 50 + spacing;
invaders->push_back(invader);
spacing += 50;
}
After pushing the object invader to the list, at the end the invaders list holds pointers that are not initialized. All the pointers got lost the references. I wonder why ?
The problem, I assume, is what happens in ~Invader(). Let's simplify the example a ton:
struct A {
int* p;
A() { p = new int(42); }
~A() { delete p; }
};
A just manages a pointer. When an A goes out of scope, that pointer gets deleted. Now, what happens when we do this:
list<A> objs;
{
A newA;
objs.push_back(newA);
// newA deleted here
}
// objs has one element... but its pointer has been deleted!
The problem is that copying A (which push_back() does) just performs a shallow copy: we copy our pointer. But since A manages its own memory, we need to do a deep copy. That is:
A(const A& rhs)
: p(new int(*(rhs.p)))
{ }
That way, the copied A won't double delete the same pointer. With C++11, this can be much more easily managed with just:
struct A {
std::shared_ptr<int> p;
A() { p = std::make_shared<int>(42); }
~A() = default; // this line not even necessary
};
Here, copying A will copy the shared_ptr and both copies of A will have a valid object to point to. If you can't use C++11, you can still use boost::shared_ptr<T> for all your memory management needs. And if you can't use that, then you have to write a copy constructor that does a full copy of all your pointer elements.
Or, the simplest solution, would be to just make your container have pointers:
list<A*> objs;
objs.push_back(new A);
Then the "shallow copy" is the right thing to do, and all you need to do is remember to delete everything in the container at the end.
Your list contains Invader objects. When you store them in the list, the Invader copy-constructor is called and a copy of the variable invader is stored in the list.
Unless you define a copy-constructor, it will be default simply do a shallow-copy of your object. This may not be what you want. You should write an explicit copy-constructor to make sure that the Invader is copied correctly.
Another solution would be to dynamically allocate Invader objects using the new keyword and storing pointers in the list. If you do that, be careful to make sure that you call delete on the Invader objects in the list when you are done with them.