struct SomeStruct
{
};
class C
{
public:
C()
{
for (int i = 0; i < 100; ++i)
{
m_List.push_back(new SomeStruct);
}
}
private:
std::list<SomeStruct*> m_List;
};
Which of two variants of destructor faster (why?) for freeing memory:
~C()
{
for (auto iter = m_List.begin(); iter != m_List.end(); ++iter)
{
delete *iter;
}
}
~C()
{
while (m_List.size() != 0)
{
delete *m_List.begin();
m_List.pop_front();
}
}
First one is faster. Second one is deleting the head and removing it as well. You don't need to remove the list elements explicitly. The complete list is anyway be destroyed as it is member of C
Related
CheckedArray::CheckedArray(int size) :mSize(size){
int *mArray = new int[size];
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
CheckedArray::~CheckedArray() {
if (mArray == NULL){
return;
}
else {
delete[] mArray;
}
}
I'm using dynamic memory allocation to create a new array. I want to check if the pointer is null, then return. If not, then delete. I'm getting these error messages, but I have no idea what's wrong.
(9094,0x100094600) malloc: *** error for object 0x10001e7b3: pointer being freed was not allocated
(9094,0x100094600) malloc: *** set a breakpoint in malloc_error_break to debug
To be completely clear
CheckedArray::CheckedArray(int size) :mSize(size){
int *mArray = new int[size];
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
should be
CheckedArray::CheckedArray(int size) :mSize(size), mArray(new int[size]){
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
Your version creates a local variable mArray which shadows the class variable of the same name.
Here is an example of what std::unique_ptr can do for you :
#include <iostream>
#include <algorithm>
#include <memory>
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r11-avoid-calling-new-and-delete-explicitly
// so use https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
// prefer range based for loops, they can't go out of bounds : https://en.cppreference.com/w/cpp/language/range-for
class dynamic_int_array_t final
{
public:
// creates an "empty array" with enough memory for 4 ints.
dynamic_int_array_t() :
m_capacity{ 4ul }, // start with a capacity for 4 ints.
m_size{ 0ul }, // but actually none are stored yet
m_values{ std::make_unique<int[]>(m_capacity) }
{
}
// allows you to construct an array from a list of integers
dynamic_int_array_t(std::initializer_list<int>&& values) :
m_capacity{ values.size() },
m_size{ values.size() },
m_values{ std::make_unique<int[]>(m_capacity) }
{
std::copy(values.begin(), values.end(), m_values.get());
}
~dynamic_int_array_t() = default; // destructor will destruct unique_ptr and free memory
// non-copyable non-movable (simplifies things for now)
dynamic_int_array_t(const dynamic_int_array_t&) = delete;
dynamic_int_array_t& operator=(const dynamic_int_array_t&) = delete;
dynamic_int_array_t(dynamic_int_array_t&&) = delete;
dynamic_int_array_t& operator=(dynamic_int_array_t&&) = delete;
// begin and end allow range based for loops to work
// range based for loops don't allow you to go out of bounds.
auto begin() const
{
return m_values.get();
}
// end should point "one past" the array (that's how end works)
auto end() const
{
int* ptr = begin();
ptr += m_size;
return ptr;
}
std::size_t size() const
{
return m_size;
}
void add(const int value)
{
// if not enough memory then allocate more
if (m_size == m_capacity) grow_capacity();
// add new value at the end
m_values[m_size] = value;
m_size++;
}
// add another array to this one
void append(const dynamic_int_array_t& rhs)
{
for (int value : rhs)
{
add(value);
}
}
private:
void grow_capacity()
{
m_capacity *= 2;
// allocate new memory
auto tmp = std::make_unique<int[]>(m_capacity);
// copy content to new memory
std::copy(begin(), end(), tmp.get());
// swap new memory with tmp so m_values will now be the newly allocated memory and tmp will hold the previously allocated memory
std::swap(tmp, m_values);
// tmp will go out of scope and delete old buffer
}
std::size_t m_capacity;
std::size_t m_size;
std::unique_ptr<int[]> m_values;
};
int main()
{
dynamic_int_array_t array{ 4,5 };
for (int n = 10; n < 20; ++n)
{
array.add(n);
}
for (const int value : array)
{
std::cout << value << " ";
}
return 0;
}
I have a class that has this kind of structure:
class myClass(){
public:
myClass(){//empty constructor}
void insertRecursively(string word) {
myClass* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new myClass();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
private:
unordered_map<char, myClass*> map = {};
bool isEnd = false;
}
I tried write destructor in this way but it gives me error 'std::bad_alloc':
~myClass() {
clear(map);
}
void clear(unordered_map<char, myClass*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
From what I known so far, I allocated memory on heap by using new keyword, so I should create destructor for myClass. I need to do this recursively because map contains pointers to other myClass pointers.
I've researched several hours and still cannot figure it out.
Can anyone help me to spot the problem that will cause 'std::bad_alloc' ?
My entire code:
class Trie {
public:
Trie() {
}
void insert(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
node->map[word.at(i)] = new Trie();
}
node = node->map[word.at(i)];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this;
for (int i = 0; i < word.length(); i++) {
if (node->map.find(word.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[word.at(i)];
}
}
return node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = this;
for (int i = 0; i < prefix.length(); i++) {
if (node->map.find(prefix.at(i)) == node->map.end()) {
return false;
} else {
node = node->map[prefix.at(i)];
}
}
return true;
}
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
private:
unordered_map<char, Trie*> map = {};
bool isEnd = false;
};
I draw your attention to the following lines of code.
unordered_map<char, myClass*> map = {};
void clear(unordered_map<char, myClass> map)
Trimming a few characters...
unordered_map<char, myClass*> map
void clear(unordered_map<char, myClass> map)
Observe that you declared the local item map over char and myClass*, but you declared clear() as operating on a map over char and myClass, as opposed to a map over char and myClass*.
The * makes a difference.
Either you start to use smart pointers or you need to bring up a concept of ownership for your myClass instances created with new.
The latter could be:
Instances aof myClass are owned by another instance of myClass (lets call it owner) where &a appears in owner.map (You are doing this already).
Whenever an instance owner becomes destroyed, it has to free all instances it (directly) owns:
~myClass() {
for(const auto& pair : map){
delete pair.second;
}
}
Your Trie::clear() method deletes the same Trie twice.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
if (pair.second != nullptr) {
clear(pair.second->map);
}
delete pair.second;
}
}
The destructor ~Trie calls clear(pair.second->map) and then calls delete pair.second which is the same as clear(pair.second->map) again since delete calls the destructor of the pointed-at data. Calling clear() twice on the same map means that the second call is trying to delete already deleted data, which causes the crash. Calling delete on a pointer does not change the value of the pointer, which is why the nullptr check does nothing.
Since the destructor calls clear(), the clear() method does not need to explicitly call itself recursively. Just delete the map pointers.
~Trie() {
clear(map);
}
void clear(unordered_map<char, Trie*> map) {
for (auto& pair : map) {
delete pair.second;
}
}
By the way, calling delete nullptr is fine since it is required to do nothing.
I need a cache of allocated objects that I could reuse like a free list of objects. Is anything wrong with my approach using a vector?
template <class T>
class Cache
{
private:
vector<T> objects;
vector<T *> freelist;
public:
Cache(int max_size) : objects(max_size)
{
for (int i = 0; i < objects.size(); ++i)
{
freelist.push_back(&objects[i]);
}
}
public
T *GetFree()
{
T *retval = nullptr;
if (freelist.size() > 0)
{
retval = freelist[freelist.size() - 1];
freelist.pop_back();
}
return retval;
}
public
void Release(T *ptr)
{
freelist.push_back(ptr);
}
};
Does this work?
I have structure:
struct node
{
bool data;
node* l;
node* r;
node(bool data_) : data(data_), l(0), r(0) {}
};
And loop like this
void printNode(std::vector<node*> nodes, int level, int max_level)
{
for (int i = 0; i < nodes.size(); i++) {
node * itr = nodes.at(i);
if (itr->data != 2) {
cout << itr->data;
newNodes.push_back(itr->l);
newNodes.push_back(itr->r);
} else {
newNodes.push_back(new node(2));
newNodes.push_back(new node(2));
cout << " ";
}
printWhitespaces(betweenSpaces);
}
}
Some times itr->l(or r) is null, not init struct. How i can check this ?
Something like this? It will skip your NULL elements, and elements with NULL value for r member of the vector and continue the for loop.
node * itr = nodes.at(i);
if(!itr || !itr->r) continue;
To check if a pointer is null simply use:
itr->l == 0
If it is 0 it is null. But consider using smart pointers, they are much safer.
You should also considering iterating over your vector the standard way, using something like this:
std::vectoc<node*>::iterator
it = nodes.begin(),
ite = nodes.end();
for(; it != ite; ++it) {
...
}
And finally you should probably be passing your vector by reference like this:
void printNode(std::vector<node*>& nodes, int level, int max_level)
I have a technical problem and it's really confusing me. I apologise in advance because I may not be giving the relevant details; I don't yet why it's going wrong and it would be excessive to include all the code I'm working with.
I'm working with a large program that uses the C++ STL. I'm moving this code to a very sensitive environment without a standard clib nor STL implementaton; it will redefine malloc/free/new/delete etc... For that, I need to replace the std:: parts with my own simplified implementations. I've started with std::vector. Right now it's running in the standard ecosystem so it's the GNU libc and STL. The only thing that's changed is this vector class.
When I execute the program with the replaced class, it segfaults. I've put this through GDB and found that the program will request an object from the vector using the subscript operator. When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class.
I'm really not sure at all what the problem is here. I would love to provide additional details, but I'm not sure what more I can give. I can only assume something is wrong with my vector implementation because nothing else in the program has been changed. Maybe there's something obvious that I'm doing wrong here that I'm not seeing at all.
I'm using: g++ (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6)
I'd really appreciate any feedback/advice!
#ifndef _MYSTL_VECTOR_H_
#define _MYSTL_VECTOR_H_
#include <stdlib.h>
#include <assert.h>
typedef unsigned int uint;
namespace mystl
{
/******************
VECTOR
********************/
template <typename T>
class vector
{
private:
uint _size;
uint _reserved;
T *storage;
void init_vector(uint reserve)
{
if (reserve == 0)
{
_reserved = 0;
return;
}
storage = (T*)malloc(sizeof(T)*reserve);
assert(storage);
_reserved = reserve;
}
public:
vector()
{
// std::cerr << "default constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
}
vector(const vector<T> &other)
{
// std::cerr << "copy constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(other.size());
_size = other.size();
for (uint i=0; i<other.size(); i++)
{
storage[i] = T(other[i]);
}
}
vector(uint init_num, const T& init_value)
{
// std::cerr << "special constructor1 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
for (size_t i=0; i<init_num; i++)
{
push_back(init_value);
}
}
vector(uint init_num)
{
// std::cerr << "special constructor2 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
}
void reserve(uint new_size)
{
if (new_size > _reserved)
{
storage = (T*)realloc(storage, sizeof(T)*new_size);
assert(storage);
_reserved = new_size;
}
}
void push_back(const T &item)
{
if (_size >= _reserved)
{
if (_reserved == 0) _reserved=1;
reserve(_reserved*2);
}
storage[_size] = T(item);
_size++;
}
uint size() const
{
return _size;
}
~vector()
{
if (_reserved)
{
free(storage);
storage = NULL;
_reserved = 0;
_size = 0;
}
}
// this is for read only
const T& operator[] (unsigned i) const
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
T& operator[] (unsigned i)
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
// overload = operator
const vector<T>& operator= (const vector<T>& x)
{
// check for self
if (this != &x)
{
_reserved = 0;
_size = 0;
storage = NULL;
init_vector( x.size() );
for(uint i=0; i<x.size(); i++)
{
storage[i] = T(x[i]);
}
_size = x.size();
}
return *this;
}
uint begin() const
{
return 0;
}
void insert(uint pos, const T& value)
{
push_back(value);
if (size() == 1)
{
return;
}
for (size_t i=size()-2; i>=pos&& i>=0 ; i--)
{
storage[i+1] = storage[i];
}
storage[pos] = value;
}
void erase(uint erase_index)
{
if (erase_index >= _size)
{
return;
}
//scoot everyone down by one
for (uint i=erase_index; i<_size; i++)
{
storage[i] = storage[i+1];
}
_size--;
}
void erase(uint start, uint end)
{
if (start > end)
{
assert(false);
}
if (end > _size)
end = _size;
for (uint i=start; i<end; i++)
{
erase(start);
}
assert(false);
}
void clear()
{
erase(0,_size);
}
bool empty() const
{
return _size == 0;
}
}; //class vector
}
#endif // _MYSTL_VECTOR_H_
Wow!
Your assignment operator also leaks memory.
Becuause you are using malloc/release the constructor to your type T will will not be called and thus you can not use your vector for anything except the most trivial of objects.
Edit:
I am bit bored this morning: Try this
#include <stdlib.h> // For NULL
#include <new> // Because you need placement new
// Because you are avoiding std::
// An implementation of swap
template<typename T>
void swap(T& lhs,T& rhs)
{
T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
template <typename T>
class vector
{
private:
unsigned int dataSize;
unsigned int reserved;
T* data;
public:
~vector()
{
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Because we use placement new we must explicitly destroy all members.
data[loop].~T();
}
free(data);
}
vector()
: dataSize(0)
, reserved(10)
, data(NULL)
{
reserve(reserved);
}
vector(const vector<T> &other)
: dataSize(0)
, reserved(other.dataSize)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// Because we are using malloc/free
// We need to use placement new to add items to the data
// This way they are constructed in place
new (&data[loop]) T(other.data[loop]);
}
}
vector(unsigned int init_num)
: dataSize(0)
, reserved(init_num)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// See above
new (&data[loop]) T();
}
}
const vector<T>& operator= (vector<T> x)
{
// use copy and swap idiom.
// Note the pass by value to initiate the copy
swap(dataSize, x.dataSize);
swap(reserved, x.rserved);
swap(data, x.data);
return *this;
}
void reserve(unsigned int new_size)
{
if (new_size < reserved)
{ return;
}
T* newData = (T*)malloc(sizeof(T) * new_size);
if (!newData)
{ throw int(2);
}
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Use placement new to copy the data
new (&newData[loop]) T(data[loop]);
}
swap(data, newData);
reserved = new_size;
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Call the destructor on old data before freeing the container.
// Remember we just did a swap.
newData[loop].~T();
}
free(newData);
}
void push_back(const T &item)
{
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Place the item in the container
new (&data[dataSize++]) T(item);
}
unsigned int size() const {return dataSize;}
bool empty() const {return dataSize == 0;}
// Operator[] should NOT check the value of i
// Add a method called at() that does check i
const T& operator[] (unsigned i) const {return data[i];}
T& operator[] (unsigned i) {return data[i];}
void insert(unsigned int pos, const T& value)
{
if (pos >= dataSize) { throw int(1);}
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Move the last item (which needs to be constructed correctly)
if (dataSize != 0)
{
new (&data[dataSize]) T(data[dataSize-1]);
}
for(unsigned int loop = dataSize - 1; loop > pos; --loop)
{
data[loop] = data[loop-1];
}
++dataSize;
// All items have been moved up.
// Put value in its place
data[pos] = value;
}
void clear() { erase(0, dataSize);}
void erase(unsigned int erase_index) { erase(erase_index,erase_index+1);}
void erase(unsigned int start, unsigned int end) /* end NOT inclusive so => [start, end) */
{
if (end > dataSize)
{ end = dataSize;
}
if (start > end)
{ start = end;
}
unsigned int dst = start;
unsigned int src = end;
for(;(src < dataSize) && (dst < end);++dst, ++src)
{
// Move Elements down;
data[dst] = data[src];
}
unsigned int count = start - end;
for(;count != 0; --count)
{
// Remove old Elements
--dataSize;
// Remember we need to manually call the destructor
data[dataSize].~T();
}
}
unsigned int begin() const {return 0;}
}; //class vector
With your current memory handling, this vector would only work with plain old data types.
To handle all types, it must ensure that objects
are actually created (malloc doesn't do that),
destroyed (free doesn't do that),
and you can't reallocate memory with realloc, because complex objects are not guaranteed to remain valid if they are byte-wise copied to another location.
Looks like the answer can be found in your question: "When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class."
You probably store base class instance T in the vector, but make push_back for the instance of the class inherited from T. In push_back {storage[_size] = T(item);} you cast (actually make copy constructor T:T(const T&)) item to T (this probably named 'type cut'), then get reference to T and invoke a method of the class inherited from T using virtual table of T where the method is not defined yet/abstract. Am I right?
To make it properly work you should put T* in the vector or shared_ptr/unique_ptr depending on the ownership terms you apply to vector elements.
Generally in vector you can store only POD (Plain Old Data) types.