I'm doing an assignment in c++ where we have to rewrite the std::vector class that inherits from an abstract Container class. We have to write methods for 'Vector' such as begin(), end(), push_back(), etc...
Many of the basic std::vector Member functions found here: http://www.cplusplus.com/reference/vector/vector/
My question is multi-faceted mainly because I am a beginner and the memory of computers is fascinating. Also, sorry if this question is super specific or has similar answers on here but I'm really stuck because this is the first time I've dealt with memory.
Here are my Container and Vector class implementations:
template <typename T>
class Container {
protected:
T* elem;
int cap;
public:
virtual T& operator[](int i) = 0;
virtual int size() = 0;
~ Container() { delete[] elem; }
};
template <typename T>
class Vector: public Container<T> {
protected:
using Container<T>::elem;
using Container<T>::cap;
private:
int sz;
public:
// constructors
Vector(); // default constructor
Vector(int n); // fill constructor #1
Vector(int n, T fill_val); // fill constructor #2
/* // ~ Vector(); // destructors
unsure if the above is correct, because of the Container destructor
*/
// overloaded operators
T& operator[](int i); // subscript overloading '[]'
T& operator=(const Vector<T>& V); // assignment overloading '='
// access methods
T& front(); //returns a reference to the first element
T& back(); //returns a reference to the last element
// iterator methods
T* begin(); // returns memory address of the first element
T* end(); // returns memory address at the end
// size methods
int size() { return sz; }
int capacity() { return cap; }
};
Part 2.
When/why do I need the destructor? I've tried to read up on destructors on Stack, but I feel more confused than clarified. I'm assuming the SegFault is coming from memory needing to be deallocated, and I know I'm supposed to be learning how to dynamically allocate memory with this assignment. The constructors and destructors are crucial for this process but can someone break it down as simply as possible for me?
EDIT:
I've fixed the segmentation fault by implementing Fureeish's answer. My code has been updated above.
The only remaining inquiry is to the destructor and its purpose. Do I need to implement one for Vector or will it be called automatically by Container?
You decided to manage the memory by yourself, but you never allocated any. You're missing some news here and there. For example, given a:
Vector(int n, T fill_val) {
sz = n;
cap = 2*n;
for(int i = 0; i < sz; i++) {
elem[i] = fill_val;
}
}
the for() loop iterates over elem, which is an uninitialized pointer. You treat it like a pointer to a dynamically allocated array, but it simply points to some garbage value. Remember - first allocate, then work on (only when you decide to manage the memory yourself. Typically you should prefer using standard implementations).
The correct version should look like this:
Vector(int n, T fill_val) {
sz = n;
cap = 2*n;
elem = new T[cap]; // or cap-1, depending on what you do with `end()`.
for(int i = 0; i < sz; i++) {
elem[i] = fill_val;
}
}
The fact that you never encountered this problem while using other constructors is just you being extremely lucky and undefined behavior being sneaky
Related
I believe this is a simple question with probably a not simple answer.
Here is the code:
template<typename T>
T* copy(T* original, int size) {
T* result = new T[size];
// At this point the default constructor of all new T objects have been called.
for(int i = 0; i < size; ++i) {
// This will call the assignment operator= on all new T objects
result[i] = original[i];
}
return result;
}
Question:
Is there a way to initialize the newly allocated memory using the copy constructor of T instead of using default constructor followed by assignment operator?
The purpose is to copy each element to its analogous element in the new array using the copy constructor of T.
I imagine there could be a way to do that by allocating memory using malloc, then calling the copy constructor for each element but I don't know how.
Here is an example solution from my imagination. If this is correct or this is the best we can get, tell me. Or propose a better solution:
template<typename T>
T* copy(T* original, int size) {
T* result = malloc(sizeof(T)*size);
// At this point the default constructor of all new T objects have been called.
for(int i = 0; i < size; ++i) {
T t(original[i]);
memcpy(result+i*sizeof(T), &t, sizeof(T));
}
return result;
}
Note: Raw pointers are being used for simplicity.
Note 2: I don't need a vector. This pattern will be used to copy the underlying data structure of more complicated objects.
You will have to allocate memory by any other means, but keep in mind that size * sizeof(T) can overflow. std::allocator takes care of this.
Use std::uninitialized_copy/std::uninitialized_copy_n to perform the copy:
template<typename T>
T* copy(T* original, int size) {
std::allocator<T> alloc;
T* result = alloc.allocate(size);
try {
std::uninitialized_copy_n(original, size, result);
} catch (...) {
alloc.deallocate(result, size);
throw;
}
return result;
}
Later you can use std::destroy/std::destroy_n to destroy them and deallocate memory:
template<typename T>
void destroy(T* ptr, int size)
{
std::destroy_n(ptr, size);
std::allocator<T>().deallocate(ptr, size);
}
This should work unless you need to be able to delete them with operator delete[] - in which case there is no solution for this.
If you are implementing a custom container, you can use template allocator like standard containers do:
template<typename T, typename Allocator = std::allocator<T>>
struct container
{
[[no_unique_address]] Allocator allocator;
...
};
For the new operator I don't think so.
But yes there is. It's called std::vector:
template<typename T>
std::vector<T> copy(T* original, int size) {
return std::vector<T>{original, original + size};
}
Because you don't follow RAII and use owning raw pointers your code is not memory leak free, so don't do that! Use C++ properly.
I am training to code an harder project that requires the implementation of a Binary search tree.
My work made me focus on custom types and templates.
Now my idea was to create a custom type array that is templated to accept any type of elements.
I even included an iterator, just to check how it worked.
Then I created a custom class called MyPairs(since the BST accepts pairs {key,value}).
The fact is that when I try to implement an array of such type I get errors like std::bad_array_new_lenght
or std::bad_alloc.
I am sure that it all is caused by the dinamic array initialization, but I can't figure out what is the problem. Sometimes the code doesn't work even for type int. What I am doing wrong?
I report the code I made. You can find more comments on what I have tried to do.
template<typename T>
class array{ //third custom type, a regular array that is created in the heap
T* start;
std::size_t size;
const int dimension = 10;//i decided, to simplify things to fix the array dimension
public:
array()
:start{new T[dimension]}, size{0} {}////////////////HERE SEEMS TO BE THE PROBLEM
~array()
{
std::cout<<"Destructor Called"<<std::endl;
delete[] start;
}
MyIterator<T> insert(T item){
if (size == dimension-1)
throw TooFullException{"Array is too small"};
start[size] = item;
MyIterator<T> x{start+size};
++size;
std::cout<<"size:"<<size<<std::endl;
return x;
}
MyIterator<T> begin(){
MyIterator<T> x{start};
return x;
}
MyIterator<T> end(){
MyIterator<T> x{start+size};
return x;
}
friend
std::ostream& operator<<(std::ostream& os, array& x)//printing the array
{
for(MyIterator<T> i = x.begin(); i != x.end(); ++i)
os<<*i<<" ";
os<<std::endl;
return os;
}
EDIT: I restricted the code so now you can see where the error is
I found someone answered my question.Since they deleted the post(I don't know why since it was correct and helpful) I will report it here.
In the end I just had to reorder the elements of the class List, according to the initialization order.
class array{
const int dimension = 10;//i decided, to simplify things to fix the array dimension
T* start;
std::size_t size;
public:
array()
:start{new T[dimension]}, size{0} {}//Now this works
...
Now it works! Thanks to the one who posted the answer first
Using C++, I am trying to create an array that holds pointers to objects I'm storing. But when the array is full, I want to expand the array.
the easy option is to allocate a new array with bigger size, then copy the elements to it, this is quite inefficient, and I thought of another way I want to try to do it:
create array of fixed size X
When full, create a new array, and make the end of the first array point to the start of the first element
Repeat as long as needed
What methods can I use to do that? I thought of one way to do it, but it seems very hacky:
declare all my new array as pointers to object pointer, then reinterprit_cast the filled elements to object pointer.
Note: I know I can use Vector, but I am instructed not to use std library.
Kind Regards,
There are some good answers in the comments already. I just want to provide a way to achieve exactly the behavior you described.
Since the elements of the array are pointers as well, you can define a union as the element of your array like this:
template<typename T>
union Cell
{
T* pElm;
Cell* pNext;//A fixed size array of Cells
}
And then build your array on top of it. For example:
template<typename T>
class SpecialArray
{
public:
//the next pointer is included
static const size_t ARRAY_LEN = 1000;// For example
using Pointer = T*;
using Segment = Cell<T>[ARRAY_LEN];
protected:
Segment* pFirst;
size_t mSize;
public:
SpecialArray()
:pFirst(nullptr),mSize(0){}
SpecialArray(SpecialArray&&){}
~SpecialArray(){}
Pointer& operator[](size_t index)
{
Segment* seg = pFirst;
size_t offest = 0;
//Search logic...
return seg[offest]->pElm;
}
const Pointer& operator[](size_t index) const;
};
Using C++, I am trying to create an array that holds pointers to
objects I'm storing. But when the array is full, I want to expand the
array.
With C++ templates and C primitives we can improvise a simple vector like below. And the grow buffer strategy is to double the size when the threshold is met.
#include <iostream>
#include <stdlib.h>
template <typename T>
class MyVector
{
public:
MyVector() : m_count(0), m_size(0), m_buffer(0)
{
m_size = bufInitSize;
m_buffer = (T*)malloc(sizeof(T) * bufInitSize);
}
~MyVector()
{
if (m_buffer)
free(m_buffer);
}
void add(const T& p)
{
if (m_count + 1 >= m_size)
{
m_size *= 2;
m_buffer = (T*)realloc(m_buffer, sizeof(T) * m_size);
}
m_buffer[m_count ++ ] = p;
}
T& operator[](int idx)
{
return m_buffer[idx];
}
private:
static const int bufInitSize = 1024;
T* m_buffer;
int m_count;
int m_size;
};
void main()
{
// using MyVector
MyVector<int*> vctOfIntPtr;
int n = 100;
vctOfIntPtr.add(&n);
int* pN = vctOfIntPtr[0];
std::cout << *pN;
}
OK, so I recently learned that (a) std::vector uses contiguous memory by definition/standard, and thus (b) &(v[0]) is the address of that contiguous block of memory, which you can read/write to as an old-skool C-array. Like...
void printem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) std::cout << iary[i] << std::endl; }
void doublem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) iary[i] *= 2; }
std::vector<int> v;
for (size_t i=0; i<100; ++i) v.push_back(i);
int* iptr = &(v[0]);
doublem(v.size(), iptr);
printem(v.size(), iptr);
OK, so that's cool, but I want to go in the other direction. I have lots and lots of existing code like
double computeSomething(const std::vector<SomeClass>& v) { ... }
If I have a C-array of objects, I can use such code like this:
SomeClass cary[100]; // 100*sizeof(SomeClass)
// populate this however
std::vector<SomeClass> v;
for (size_t i=0; i<100; ++i) v.push_back(cary[i]);
// now v is also using 100*sizeof(SomeClass)
double x = computeSomething(v);
I would like to do that (a) without the extra space and (b) without the extra time of inserting a redundant copy of all that data into the vector. Note that "just change your stupid computeSomething, idiot" is not sufficient, because there are thousands of such functions/methods that exhibit this pattern that are not under my control and, even if they were are too many to go and change all of them.
Note also that because I am only interested in const std::vector& usage, there is no worry that my original memory will ever need to be resized, or even modified. I would want something like a const std::vector constructor, but I don't know if the language even allows special constructors for const instances of a class, like:
namespace std { template <typename T> class vector {
vector() { ... }
vector(size_t n) { ... }
vector(size_t n, const T& t) { ... }
const vector(size_t n, T*) { ... } // can this be done?
...
If that is not possible, how about a container derived off of std::vector called std::const_vector, which (a) could construct from a pointer to a c-array and a size, and (b) purposefully did not implement non-const methods (push_back, resize, etc.), so then even if the object with a typename of const_vector is not actually a const object, the interface which only offers const methods makes it practically const (and any erroneous attempts to modify would be caught at compile time)?
UPDATE: A little messing around shows that this "solves" my problem wrt Windows-implementation of std::vector:
template <typename T>
class vector_tweaker : public std::vector<T> {
public:
vector_tweaker(size_t n, T* t) {
_saveMyfirst = _Myfirst;
_saveMylast = _Mylast;
_saveMyend = _Myend;
_Myfirst = t;
_Mylast = t + n;
_Myend = t + n;
}
~vector_tweaker() {
_Myfirst = _saveMyfirst;
_Mylast = _saveMylast;
_Myend = _saveMyend; // and proceed to std::vector destructor
}
private:
T* _saveMyfirst;
T* _saveMylast;
T* _saveMyend;
};
But of course that "solution" is hideous because (a) it offers no protection against the base class deleting the original memory by doing a resize() or push_back() (except for a careful user that only constructs const vector_tweaker()) -- and (b) it is specific to a particular implementation of std::vector, and would have to be reimplemented for others -- if indeed other platforms only declare their std::vector member data as protected: as microsoft did (seems a Bad Idea).
You can try reference-logic storing introduced in C++11 with std::reference_wrapper<>:
SomeClass cary[100];
// ...
std::vector<std::reference_wrapper<SomeClass>> cv;
cv.push_back(cary[i]); // no object copying is done, reference wrapper is stored
Or without C11, you can create a specialization of such template class for bytes - char. Then for the constructor from char* C-array you can use ::memcpy: which unfortunately will then use twice as much memory.
::memcpy(&v[0], c_arr, n);
Something like this:
template <typename T> class MyVector : public std::vector<T> {
};
template <> class MyVector<char> : public std::vector<char> {
public:
MyVector<char>(char* carr, size_t n) : std::vector<char>(n) {
::memcpy(&operator[](0), carr, n);
}
};
What I would recommend - replace all C-arrays to vectors where possible, then no extra copying will be needed.
I've been working on all the exercises in Bjarne Stroustrup's "The C++ Programming Language' book.I've come to one particular exercise (E.8.4) that has me stumped. This is the appendix that discusses exception safety in standard library classes. He illustrates one possible implementation of a vector's constructor and then asks the reader to find the error. The hint indicates that it has something to do with the destructor (a double free somehow maybe?) but I'm just not seeing what he's after.
From what I can tell, the allocator could throw bad_alloc, terminating the ctor. Similarly, a copy ctor on T could throw inside uninitialized_fill, which would destruct any previously copied elements and terminate the ctor. If there's an error in there, it's not obvious to me.
The exercise is worded as follows: "Find the error in the 'messy' version of vector's constructor (E.3.1) and write a program to get it to crash. Hint: First implement vector's destructor.
This is only a 1 point exercise, so I must be missing something stupidly obvious. I assume it has nothing to do with destructors throwing, as all bets are off in that case. Maybe there's an issue maintaining an invariant with the "space" and "last" fields?
I'd love to hear anyone's ideas.
Here is the associated code:
template<class T, class A = std::allocator<T> >
class vector {
private:
T* v;
T* space;
T* last;
A alloc;
void destroy_all();
public:
typedef size_t size_type;
explicit vector(size_type n, const T& val = T(), const A& = A());
vector(const vector& a);
vector& operator=(const vector& a);
~vector() { destroy_all(); alloc.deallocate(v, last-v); }
size_type size() const { return space-v; }
size_type capacity() const { return last-v; }
void push_back(const T&);
};
template<class T, class A>
void vector<T,A>::destroy_all() {
for(T* p = v; p != last; p++)
alloc.destroy(p);
}
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) : alloc(a) {
v = alloc.allocate(n);
try {
std::uninitialized_fill(v, v + n, val);
space = last = v + n;
}
catch(...) {
alloc.deallocate(v, n);
throw;
}
}
The problem is that the constructor for T can throw, meaning that this can throw:
std::uninitialized_fill(v, v + n, val);
When that happens, the catch(...) clause deallocates memory without properly destroying any T objects that may have been already constructed.
allocator::allocate can throw std::bad_alloc. In that case you have uninitialized v and even if you recover from insufficient memory, you will segfault on destruction.
Another problem is with std::initialized_fill. If it throws, you free the internal storage without marking the data pointer as invalid. The destructor will free the same memory again.