I'm starting to learn c++ but I'm stuck in the destructor. We need to implement a vector and this is what I have so far.
#include<string.h>
#include<cassert>
#include<iostream>
using namespace std;
template<class T>
class Vector {
template<class U> friend ostream& operator<<(ostream&, const Vector<U>&);
private:
T* data;
unsigned len;
unsigned capacity;
public:
Vector(unsigned = 10);
Vector(const Vector<T>&);
virtual ~Vector(void);
Vector<T>& operator =(const Vector<T>&);
bool operator==(const Vector<T>&);
T& operator[](unsigned);
};
//PROBLEM!
template <class T>
~ Vector() {
delete data;
}
template<class T>
Vector<T>::Vector(unsigned int _capacity)
{
capacity = _capacity;
len = _capacity;
data = new T[_capacity];
}
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
len = v.len;
capacity = v.capacity;
data = new T[len];
for (unsigned int i = 0; i < len; i++)
data[i] = v.data[i];
}
template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
delete[ ] data;
len = v.len;
capacity = v.capacity;
data = new T [len];
for (unsigned int i = 0; i < len; i++)
data[i] = v.data[i];
return *this;
}
template<class T>
bool Vector<T>::operator == (const Vector<T> & v)
{
bool check = true;
check &= (len == v.len);
if (!check) return false;
check &= (capacity == v.capacity);
if (!check) return false;
for (unsigned int i = 0; i < len; i++) {
check &= (data[i] == v.data[i]);
if (!check) return false;
}
return true;
}
template<class T>
T& Vector<T>::operator[](unsigned int index)
{
return data[index];
}
The interface is given and I need to implement it. But this is so different from C and Java, that I'm a bit lost.
In the second exercise we need to implement something like this using a) the previous Vector implementation as derived class and b) the Vector as composition class, so maybe we will use the virtual destructor in one of the approaches?
void testAssociativeArray() {
AssociativeArray<String, int> table;
table["abc"] = 15;
table["jkl"] = 12;
table["xyz"] = 85;
assert(table["jkl"], 12);
}
template<class P, class Q>
class Pair {
P p;
Q q; public:
Pair(const P& _p = P(), const Q& _q = Q()): p(_p), q(_q) {}
P& objectP() {return p;}
Q& objectQ() {return q;}
};
First off, why do you think that the destructor should be virtual? Are you using polymorphism?
Second, you are using delete incorrectly for your array.
Since you used:
data = new T[length];
You must use the array syntax:
delete [] data;
Third, you need to put the namespace in front of all of your class function definitions:
template <class T>
Vector<T>::~Vector()
{
delete [] data;
}
And just for your information, you declare the destructor like so...
virtual ~Vector(void);
As I mentioned, virtual is unnecessary unless you are using this class as a base or derived class in a polymorphic manner. For more information on when you need to use virtual destructors, look at the answer to this question.
In addition, the void in the parameters is also unnecessary. This used to be required in old C standard, but it is not in C++.
You should be able to declare it like so:
~Vector();
If you define AssociativeArray<P,Q> with a has-a relationship to Vector<T>, then you can simply make the class contain a Vector<Pair<P,Q> >. Declaring virtual methods in this case, are not needed, but can still be used--with some extra overhead.
If you define AssociativeArray<P,Q> with a is-a relationship to Vector<Pair<P,Q> >, then you should define some virtual methods in Vector<T>, including a virtual destructor.
The use of virtual methods only matters when using objects polymorphically through pointers and references. See this page.
AssociativeArray<String,Int>* myDerivedMap = new AssociativeArray<String,Int>();
delete myDerivedMap; //NO virtual methods necessary here. using a pointer to derived class
Vector<Pair<String,Int> >* myBaseMap = new AssociativeArray<String,Int>();
delete myBaseMap; //virtual methods ARE necessary here. using a pointer to base class
template<class T>
Vector<T>::~Vector()
{
delete [] data;
}
Note that you must use delete [] and not delete
Should be
template <class T>
Vector<T>::~Vector() {
delete[] data;
}
Related
I create a class Vector that contain two variables that are template variable, I am trying to build such a dictionary that tell mark of a specific student.
The problem is I am struggling dynamic allocating memory with template. I have to do this without map or STL help. Can you explain, how to allocate them properly.
#define DEF_CAPACITY 20
template <class U, class T>
class Vector {
protected:
T* _data;
U* _keys;
int _size; //size in use
int _capacity; //available capacity
public:
//constructors
Vector(int capacity = DEF_CAPACITY);
~Vector();
int getSize() const { return size; }
void insert(U key, T data);
T operator[](U key);
};
template<class U, class T>
inline Vector<U, T>::Vector(int capacity)
{
this->_capacity = capacity;
this->_size = 0;
}
template<class U, class T>
Vector<U, T>::~Vector()
{
if (_data)
delete[] _data;
if (_keys)
delete[] _keys;
}
template<class U, class T>
void Vector<U, T>::insert(U key, T data)
{
_keys[_size] = new U;
//Maybe I have to do something like that, but all options I test doesn't work
//keys[_size] = new U[Some size];
_keys[_size] = key;
_data[_size] = new T;
_data[_size] = data;
_size++;
}
template<class U, class T>
T Vector<U, T>::operator[](U key)
{
int index = 0;
for (int i = 0; i < size; i++)
if (_keys[i] == key)
index = i;
return _data[index];
}
int main() {
Vector<string, int> grades;
grades.insert("john", 90);
grades.insert("marc", 100);
grades.insert("ron", 87);
grades.insert("lynda", 95);
//...
grades.insert("Tome", 93);
//...
cout << grades["marc"] << endl;
cout << grades["lynda"] << endl;
cout << grades["john"] << endl;
return 0;
}
To allocate n elements of type T you use:
T* array = new T[n];
so to allocate space for your keys and values:
// be aware: this will call the constructor for each element!!!
_keys = new U[n];
_data = new T[n];
If your "Vector" has a fixed capacity, this should be done in your constructor:
template<class U, class T>
inline Vector<U, T>::Vector(int capacity)
: _data(new T[capacity]);
, _keys(new U[capacity]);
, _size(0);
, _capacity(capacity);
{
}
after that, you can insert key/value pairs like this:
template<class U, class T>
void Vector<U, T>::insert(const U& key, const T& data)
{
if(_size == _capacity)
throw std::out_of_range("no space left!!!");
_keys[_size] = key; // copy assignment
_data[_size] = data; // copy assignment
_size++;
}
This design will unnecessarily call constructors for unused keys and values.
You could also do this with malloc and free to prevent this, though I wouldn't recommend going that way and instead use the standard library.
template<typename T>
class SmartBuffer {
int capacity;
MyArray<T> *data;
public:
SmartBuffer() {
capacity = 0;
data = nullptr;
}
void alloc(int nbElements) {
if (nbElements > capacity) {
cout << "Allocating smart buffer\n";
if (data!=nullptr) delete data;
data = new MyArray<T>(nbElements + 1);
capacity = nbElements + 1;
}
}
int size() {
return capacity;
}
MyArray<T>* getBuffer() const {
return data;
}
MyArray<T>* getBuffer() {
return data;
}
};
Basically, this class is used for pre-allocation of data, since new[] and delete[](and cudaMalloc and cudaFree) are slow and makes up a significant portion of running time in my problem.
So, I wish to return a pointer to a MyArray object, the caller can modify the object freely. But it must not be allowed to delete the data. Basically, only a SmartBuffer object can delete it's own data.
Is there anyway to do this in C++ other than having another wrapper class and remove the delete operator like below?
void operator delete (void *p) = delete;
A natual solution would be to declare the destructor of MyArray as private and add friend class SmartBuffer<T> to its definition to allow only SmartBuffer to delete it.
However as you stated in a comment you're using MyArray in different contexts and want it to act "normally". Why won't you just implement another type. The least effort is to create an inheriting "wrapper" intended for use with SmartBuffer:
template <typename T> class SmartBuffer; //forward declaration (necessary before 'friend')
template <typename T>
class MySmartArray : public MyArray<T>
{
private: ~MySmartArray() {}
private: MySmartArray() {} //disallow also the constructor from external usage
friend class SmartBuffer<T>;
};
Then you go:
template<typename T>
class SmartBuffer {
int capacity;
MySmartArray<T> *data;
public:
~SmartBuffer { delete data; } //SmartBuffer can delete MySmartArray
//and replace everywhere MyArray to MySmartArray ...
//...
};
Now an external user won't be able to delete it:
int main()
{
SmartBuffer<int> buff;
auto ptr = buff.getBuffer();
delete ptr; // error: 'MySmartArray<T>::~MySmartArray() [with T = int]' is private within this context
return 0;
}
There are some Classes: Array, NumericArray. Array is a template class, and NumericArray is a class inherited from Array designed to take int, double, etc.
Part of Header of NumericArray:
template <class T = int>
class NumericArray : public Array<T>{
private:
T* m_data;
int size;
public:
NumericArray<T> operator * (T factor)const;
};
here are constructors and some functions of NumericArray:
template <class T>
NumericArray<T>::NumericArray(){
m_data = new T[10];
size = 10;
}
template <class T>
NumericArray<T>::NumericArray(int n){
m_data = new T[n];
size = n;
}
template <class T>
NumericArray<T>::NumericArray(const NumericArray<T>& s_data){
m_data = new T[s_data.size];
// assign elements in the source array
for (int i = 0;i<=(s_data.Size()-1 ); i++){
m_data[i] = s_data.m_data[i];
}
size = s_data.Size();
}
/* Destructor */
template <class T>
NumericArray<T>::~NumericArray(){
delete [] m_data;
}
template <class T>
NumericArray<T> NumericArray<T>::operator * (T factor)const{
NumericArray<T> temp(size);
for (int i = 0; i<size;i++){
temp.m_data[i] = (*this)[i] *factor;
}
return temp;
}
And when I call it in the main(), something weird happens. For example:
NumericArray<int> intArray1(10);
NumericArray<int> intArray2;
for(int i =0; i<10;i++){
intArray1[i] = i;
intArray2[i] = i;
}
The 2 arrays do contain numbers 0-9, but if I call
NumericArray intArray4 = intArray1*2;
intArray4 consists of zero(0)s. It seems that the default constructor is called in the function and passed to Array4. And after the operator, Array1 and Array2 are still numbers 0-9
Below are the related code of Array
template class Array{
private:
T* m_data;
int size;
public:
Array(); // constructor
Array(int n); // constructor
Array(const Array<T>& s_data); //Copy Constructor
virtual ~Array(); // destructor
void SetElement(int i, const T& source);
T& GetElement(int i)const;
int Size() const;
int DefaultSize()const;
void DefaultSize(int n);
// Operator overloading
Array<T>& operator = (const Array<T>& source) ;
T& operator [](int i);
const T& operator [] (int i) const;
};
template <class T>
Array<T>::Array(){
// default constructor
m_data = new T[defaultSize]; // initialize T*
size = defaultSize; // initialize integer
}
template <class T>
Array<T>::Array(int n){
// Constructor with arguments
m_data = new T[n];
size = n;
}
template <class T>
Array<T>::Array(const Array<T>& s_data){
// Copy constructor
m_data = new T[s_data.Size()];
// assign elements in the source array
for (int i = 0;i<=(s_data.Size()-1 ); i++){
m_data[i] = T( s_data.m_data[i]);
}
size = s_data.Size();
defaultSize = s_data.Size();
}
template <class T>
T& Array<T>::operator [](int i) {
if (i > size|| i<0){
OutOfBoundsException a;
throw a;
}
return m_data[i];
}
Not sure if provided enough information. Any hint is greatly appreciated.
The easiest way to access base class data members when the base is a (dependent) class template, is a using declaration like this:
#include <iostream>
using namespace std;
template< class Item >
class Base
{
protected:
Item item_;
Base( Item const& v ): item_( v ) {}
};
template< class Item >
class Derived
: public Base< Item >
{
protected:
using Base<Item>::item_;
public:
auto value() const -> Item { return item_; }
Derived( Item const& v ): Base<Item>( v ) {}
};
auto main() -> int
{
Derived<int> const x( 42 );
cout << x.value() << endl;
}
Alternatively you can qualify the access, e.g. this->item_ or Base<Item>::item_.
That said, it’s usually not a good idea to let derived classes access base class data members directly.
Here is the problem: both NumericArray and Array have
T* m_data;
int size;
The function Array::operator[] is called from Array which uses Array::m_data, however the NumericArray::operator* sets NumericAray::m_data. It probably is working as you would expect, but you are reading from the wrong pointer.
Remove the members from NumericArray and make the members protected instead of private in Array. The second part is optional if the implementation is changed a little bit.
I am reading Programming Priciples and Practice Using C++ chapter 17-19, and trying to write my version of Vector. This is my code:
#include <stdexcept>
#include <exception>
using namespace std;
struct Range_error:out_of_range{
int index;
Range_error(int i):out_of_range("Range error"),index(i){}
};
template<class T, class A = allocator<T>> struct Vector_base{
A alloc;
T* elem;
int sz; // number of elements
int space; // number of elements plus "free space"/"slots" for new elements("the current allocation")
void copy(const Vector_base& arg)
{
T* p = alloc.allocate(arg.sz);
for(int i=0; i<arg.sz; ++i) alloc.construct(&p[i], arg.elem[i]);
elem = p;
sz = arg.sz;
space = arg.space;
};
Vector_base(): elem(alloc.allocate(0)), sz(0), space(0) {};
Vector_base(int n):elem(alloc.allocate(n)), sz(n), space(n) {};
Vector_base(const A& a, int n):alloc(a), elem(a.allocate(n)), sz(n), space(n){};
Vector_base(const Vector_base& arg) {copy(arg);}
~Vector_base() {alloc.deallocate(elem, space);}
};
template<class T, class A = allocator<T>> class Vector : private Vector_base<T,A>{
public:
Vector() : Vector_base(){};
Vector(int n) : Vector_base(n) {for(int i=0; i<this->sz; ++i) this->alloc.construct(&this->elem[i], T());}
Vector(const Vector& arg) : Vector_base(arg) {};
Vector& operator=(const Vector&);
~Vector() {};
int size() const {return this->sz;}
int capacity() const {return this->space;}
void resize(int newsize, T val=T());
void push_back(const T& val);
void pop_back(); // delete the last element
void reserve(int newalloc);
T& operator[](unsigned int n)
{
return this->elem[n];
}
const T& operator[](unsigned int n) const
{
return this->elem[n];
}
T& at(unsigned int n)
{
if(n<0 || this->sz<=n) throw Range_error(n);
return this->elem[n];
}
const T& at(unsigned int n) const
{
if(n<0 || this->sz<=n) throw Range_error(n);
return this->elem[n];
}
};
template<class T, class A> void Swap(Vector_base<T,A>& a, Vector_base<T,A>& b){
Vector_base<T,A> c(a);
a=b;
b=c;
}
template<class T, class A> Vector<T,A>& Vector<T,A>::operator=(const Vector<T,A>& a)
{
if(this == &a) return *this; // self-assignment, no work needed
if(a.sz<=sz){
for(int i=0; i<a.sz; ++i) elem[i] = a.elem[i];
sz=a.sz;
return *this;
}
T* p = new T[a.sz];
for(int i=0; i<a.sz; ++i) p[i] = a.elem[i];
delete elem;
elem=p;
space=sz = a.sz;
return *this;
}
template<class T, class A> void Vector<T,A>::reserve(int newalloc)
{
if(newalloc <= this->space) return;
Vector_base<T,A> b(this->alloc,newalloc);
for(int i=0; i<this->sz; ++i) this->alloc.construct(&b.elem[i], this->elem[i]); // copy
for(int i=0; i<this->sz; ++i) this->alloc.destroy(&this->elem[i]);
Swap<Vector_base<T,A>>(*this, b);
this->space = newalloc;
}
template<class T, class A> void Vector<T,A>::resize(int newsize, T val=T())
{
reserve(newsize);
for(int i=this->sz; i<newsize; ++i) this->alloc.construct(&this->elem[i], val);
for(int i=newsize; i<this->sz; ++i) this->alloc.destroy(&this->elem[i]);
this->sz = newsize;
}
template<class T, class A> void Vector<T,A>::push_back(const T& val)
{
if(this->space == 0) reserve(8);
else if(this->sz == this->space) reserve(2*(this->space));
this->alloc.construct(&this->elem[this->sz], val);
++(this->sz);
}
template<class T, class A> void Vector<T,A>::pop_back()
{
if(this->sz == 0) return;
this->alloc.destroy(&this->elem[--(this->sz)]);
if(this->sz <= (this->space)/2)
{
Vector_base<T,A> b(this->alloc,(this->space)/2);
for(int i=0; i<this->sz; ++i) this->alloc.construct(&b.elem[i], this->elem[i]); // copy
for(int i=0; i<this->sz; ++i) this->alloc.destroy(&this->elem[i]);
Swap<Vector_base<T,A>>(*this, b);
this->space /= 2;
}
}
when it compiled, the vc++ says "void Swap(Vector_base &,Vector_base &)' : could not deduce template argument for 'Vector_base,A> &' from 'Vector'". I know that *this is a Vector object but b is Vector_base object, but that's the book says. How can I make this code work? Is there any memory leak of this code? Thanks!
Your Swap function template is defined as template<class T, class A>.
So you should call it like: Swap<T,A>(*this, b); instead of Swap<Vector_base<T,A>>(*this, b);
And actually, calling Swap<T,A>(*this, b) is not correct at all. You should allocate memory of the requested size and copy existing elements to the new space. Then release memory that you initially allocated.
The second problem is in:
Vector_base(const A& a, int n)
: alloc(a), elem(a.allocate(n)), sz(n), space(n)
You cannot call a non-const member function on a const object.
So, use alloc.allocate(n) instead of a.allocate(n).
Update 1
Also, you're still mixing new and delete operators with alloc.allocate() and alloc.deallocate() in the assignment operator of Vector.
Update 2
Your assignment operator will be never called in Swap<T, A> because you're actually working with Vector_base, while assignment operator is defined for Vector. So Memberwise assignment will happen.
template<class T, class A> void Swap(Vector_base<T,A>& a, Vector_base<T,A>& b){
Vector_base<T,A> c(a);
a=b;
b=c;
}
That is b.elem and c.elem will point at the same address and alloc.deallocate will be called twice for it. Because first time ~Vector_base() will be called when c will go out of scope when Swap returns. And second time destructor will be called when b will go out of scope when reserve returns.
That's why you get an unhandled exception.
Is there any memory leak of this code?
Yes, there are memory leaks with your code. For instance:
void copy(const Vector_base& arg)
{
T* p = alloc.allocate(arg.sz);
for(int i=0; i<arg.sz; ++i)
alloc.construct(&p[i], arg.elem[i]); <--- what happens here
if construction fails ?
elem = p;
sz = arg.sz;
space = arg.space;
};
If the copy constructor throws, you leak the allocated memory and whatever resource the already constructed objects are holding.
A cure is:
for (int i = 0; i < arg.sz; ++i)
{
try { alloc.construct (&p[i], arg.elem[i]); }
catch (...)
{
// 1) Destroy the allocated objects
while (--i != 0) alloc.destroy (&p[i]);
// 2) Release p
alloc.deallocate (p, arg.sz);
// 3) rethrow exception
throw;
}
}
You have to do this consistently throughout the code, not only in the copy function. This is the number 1 reason why we use standard containers and not home-made ones.
For instance, exception safety is the reason why we have top and pop in std::stack: if there were only a pop method returning a copy of the object, what would happen if the copy construction throws an exception ?
If you want to implement your own containers (as an exercise or as a thoughtful decision), an excellent thing to do is to look at the implementation from your standard library. STL containers are templates, and all the code resides in the header files. Study it carefully, you'll learn many things.
I'm totally new here, so I'm not very familiar with style of writing here, so sorry if the question doesn't look like it should.
My question is, how can I create an array of object, but not with default constructors?
If I have something like this:
set<movie> a(3);
set<movie> b(2);
And constructors:
For movie
movie::movie()
{
this->naziv=0;
this->reditelj=0;
this->trajanje=0;
}
movie::movie(char *name, char *red, int len)
{
this->naziv=new char[strlen(name)+1];
strcpy(naziv,name);
this->reditelj=new char[strlen(red)+1];
strcpy(reditelj,red);
this->trajanje=len;
}
And for set:
template<class t>
set<t>::set()
{
this->br=0;
this->niz=0;
}
set<t>::set(int b)
{
this->br=b;
this->niz=new t[br];
}
Answers are great,but on course they teach us some basic stuff about c++,how to create class,template class,I mean,to write programs from the beginning,so for now we don't use that classes and functions that most of you mentioned. The assignment is to write the code this way,so how can I do that?
The assignment is to make a class and a template class,template class is actually an array of objects,so I should make an object,that's an array of objects,and some other functions.
Here's my whole code:
Set.h
#pragma once
#include<iostream>
using namespace std;
template<class t>
class set
{
int br;
t* niz;
public:
set();
set(int b);
~set();
set(set& copy);
int vrati_br_elem()
{
return br;
}
bool pripada(t elem);
set operator*(set& drugi);
friend istream& operator>> <>(istream& ulaz,set<t> &s);
friend ostream& operator<< <>(ostream& izlaz,set<t> &s);
};
template<class t>
set<t>::set()
{
this->br=0;
this->niz=0;
}
template<class t>
set<t>::set(int b)
{
this->br=b;
this->niz=new t[br];
}
template<class t>
set<t>::~set()
{
if(this->niz!=0)
delete [] niz;
}
template<class t>
bool set<t>::pripada(t elem)
{
for(int i=0;i<this->br;i++)
if(this->niz[i]=elem)
return true;
return false;
}
template<class t>
set<t> set<t>::operator *(set<t> &drugi)
{
int broj=0;
set<t> pom((this->br>drugi.br)?this->br:drugi.br);
for(int i=0;i<this->br;i++)
for(int j=0;j<drugi.br;j++)
if(this->niz[i]==drugi.niz[j])
pom.niz[broj++]=this->niz[i];
pom.br=broj;
return pom;
}
template<class t>
istream& operator>>(istream& ulaz,set<t> &s)
{
for(int i=0;i<s.br;i++)
cin>>s.niz[i];
return ulaz;
}
template<class t>
ostream& operator<<(ostream& izlaz,set<t> &s)
{
for(int i=0;i<s.br;i++)
cout<<endl<<s.niz[i]<<endl;
return izlaz;
}
template<class t>
set<t>::set(set<t> ©)
{
this->br=copy.br;
this->niz=new t[br];
for(int i=0;i<this->br;i++)
this->niz[i]=copy.niz[i];
}
movie.h
#include<iostream>
using namespace std;
class movie
{
char* naziv;
char* reditelj;
int trajanje;
public:
movie();
~movie();
movie(movie& copy);
movie(char* name,char* red,int len);
movie& operator=(movie& film);
bool operator==(movie& film);
friend istream& operator>>(istream& ulaz,movie& film);
friend ostream& operator<<(ostream& izlaz,movie& film);
};
movie.cpp
#include"movie.h"
using namespace std;
movie::movie()
{
this->naziv=0;
this->reditelj=0;
this->trajanje=0;
}
movie::~movie()
{
if(naziv!=0&&reditelj!=0)
{
delete [] naziv;
delete [] reditelj;
}
}
movie::movie(movie ©)
{
this->naziv=new char[strlen(copy.naziv)+1];
strcpy(this->naziv,copy.naziv);
this->reditelj=new char[strlen(copy.reditelj)+1];
strcpy(this->reditelj,copy.reditelj);
this->trajanje=copy.trajanje;
}
movie& movie::operator =(movie &film)
{
if(this!=&film)
{
delete [] naziv;
delete [] reditelj;
this->naziv=new char[strlen(film.naziv)+1];
strcpy(this->naziv,film.naziv);
this->reditelj=new char[strlen(film.reditelj)+1];
strcpy(this->reditelj,film.reditelj);
this->trajanje=film.trajanje;
}
return *this;
}
bool movie::operator ==(movie &film)
{
if(!strcmp(this->naziv,film.naziv)&&!strcmp(this->reditelj,film.reditelj)&&this->trajanje==film.trajanje)
return true;
return false;
}
istream& operator>>(istream& ulaz,movie& film)
{
ulaz>>film.naziv>>film.reditelj>>film.trajanje;
return ulaz;
}
ostream& operator<<(ostream& izlaz,movie& film)
{
izlaz<<endl<<film.naziv<<endl<<film.reditelj<<endl<<film.trajanje<<endl;
return izlaz;
}
movie::movie(char *name, char *red, int len)
{
this->naziv=new char[strlen(name)+1];
strcpy(naziv,name);
this->reditelj=new char[strlen(red)+1];
strcpy(reditelj,red);
this->trajanje=len;
}
Don't use built-in arrays, especially if you are new. Built-in arrays are best left to experts and even then they are often best avoided. Instead of using T[n] just use std::vector<T>. This one will start out empty an you can then e.g. push_back() the objects you are interested in.
That said, I don't see where you code excerpt actually has a problem.
You can create an array of objects invoking the constructor directly.
movie objs[2] = {movie(arg1, arg2, arg3), movie(arg1, arg2, arg3)};
The standard way to do this is to use a allocator object, like all the standard containers.
template<class T, class alloc_type =std::allocator<T> >
class set {
typedef alloc_type::pointer pointer; //bring in it's pointer type
alloc_type alloc;
And then, use that for everything:
pointer buffer = alloc.allocate(100);
alloc.construct(buffer+0); //deault construct T
alloc.construct(buffer+1, T()); //construct T from copy
alloc.construct(buffer+2, 17); //construct T from 17
alloc.destroy(buffer+2); //clean up objects
alloc.destroy(buffer+1);
alloc.destroy(buffer+0);
alloc.deallocate(buffer); //clean up buffer
Remember, it's standard to construct from lowest index to highest, and to destroy in the reverse order.
The "correct" way to do this has changed with C++11, but since I use MSVC10, which can't do the correct way, I still use this way.
Basic implementations of each of these functions is rediculously simple, though.
template<class T>
class myallocator {
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
myallocator () throw() {}
template <class U> myallocator (const myallocator<U>&) throw() {}
pointer address (reference x) const {return &x;}
const_pointer address (const_reference x) const {return &x;}
size_type max_size() const throw() {return size_type(-1);}
pointer allocate(size_type c,const_pointer h=0){return(T*)new char[sizeof(T)*c];}
void deallocate(pointer ptr, size_type c) {delete [] ptr;}
pointer construct(pointer ptr) {return new(ptr)T;}
template<class U>
pointer construct(pointer ptr, const U& from) {return new(ptr)T(from);}
void destroy(pointer ptr) {ptr->~T();}
};
The two construct members use what is called "placement new" which creates the object in an already existing space. Here, an array of chars.
You are trying to write your own container class, and you should not call it set. I will assume that you are calling it my_set in this answer.
I think this is the line you are interested in. You wish to pass in a default value here:
my_set<t>::my_set(int b, t default_value)
//creates a my_set with 'b' elements, where each element is set to default_value
Is this your goal? If so, it's easier to define niz as a *vector<t> instead of as t*
template<class t>
struct my_set {
int br;
vector<t> *niz;
my_set(int b, const t& default_value);
}
template<class t>
my_set<t>::my_set(int b, const t& default_value) {
//creates a my_set with 'b' elements, where each element is set to 0
this->br=b;
this->niz=new vector<t>(b, default_value);
}
There are other changes you may consider, such as defining niz simply as vector<t>, not as vector<t>*, but I think that's beyond the scope of you original question.
Finally, I have a question of my own for everybody. How can I do an uninitialized array new[] in C++? I'd like to new an array of known size, but with unconstructed data, and then use something like uninitialized_copy to copy data in.
One of the problems in your code is that this will fail if either string is 0
ostream& operator<<(ostream& izlaz,movie& film)
{
izlaz
<< endl << film.naziv // fails if film.naziv == 0
<< endl << film.reditelj // fails if film.reditelj == 0
<< endl << film.trajanje << endl;
return izlaz;
}
That crashes for me. You should not do cout << (char*)0;. It's better to do something like cout << "". You could fix it by changing the constructor of movie:
movie::movie()
{
this->naziv=""; // an empty, non-null, string
this->reditelj=""; // an empty, non-null, string
this->trajanje=0;
}
But a better solution is to stop using char * and use std :: string instead.
The expression new T[n] will always allocate space for n T objects and call the T constructor on each element. Similarly, delete[] niz, will always call the T destructor on each element. It seems that you want to manually control when the T constructor and destructor are called, so rather than using ::operator new[], you could just use ::operator new and its placement syntax.
You want niz to be an array of br T objects. Instead of this:
niz = new T[br];
You can use this:
niz = static_cast<T *>(::operator new(br * (sizeof (T))));
which will allocate space in the heap for br contiguous T objects, but not call the T constructor on any of them. It's basically like using malloc() to allocate space for the T objects.
But, now you have a problem: how do you actually use one of the T objects? Before you can do anything with niz[i], you need to make sure that the ith T object has been constructed. You can use placement new to construct it:
new(niz + i) T();
Notice that niz + i is the pointer to the ith T object. The effect of this statement is that the T constructor is called in place using the space at reinterpret_cast<char *>(niz + i) through reinterpret_cast<char *>(niz + i) + (sizeof (T)).
Now you have another problem: how do you keep track of which T objects have been constructed? You need to know this in order to call the destructor on the ones that have been constructed, or else you might leak memory.
One solution is to use a std::vector<bool> having br bool objects. If the ith bool is true, then you will know that the ith T object was constructed. Otherwise, it needs to be constructed.
In the set<T> destructor, you need to make sure to destroy all T objects that have been constructed. Suppose that the ith T object has been constructed. To call the T destructor on the ith T object, you can use this statement:
(niz + i)->~T();
Putting it all together, you would get something like this:
#include <cstddef>
#include <iostream>
#include <new>
#include <vector>
template <typename T>
class set
{
std::size_t br;
T *niz;
std::vector<bool> constructed;
public:
std::string name;
set()
: br(0), niz(NULL), constructed()
{
}
set(std::size_t br)
: br(br), niz(NULL), constructed(br, false)
{
niz = static_cast<T *>(::operator new(br * (sizeof (T))));
}
void destroy()
{
std::cout << "~set(" << name << ")\n";
if (niz) {
std::vector<bool>::const_iterator begin = constructed.begin(), it, end = constructed.end();
for (it = constructed.begin(); it != end; ++it) {
if (*it) {
(niz + (it - begin))->~T();
}
}
::operator delete(niz);
}
}
~set()
{
destroy();
}
set<T>& operator=(const set<T>& other)
{
if (this != &other) {
destroy();
niz = NULL;
constructed = std::vector<bool>(other.br, false);
br = other.br;
T *tmp = static_cast<T *>(::operator new(other.br * (sizeof (T))));
try {
std::size_t i;
for (i = 0; i < other.br; ++i) {
if (other.constructed[i]) {
new(tmp + i) T(other.niz[i]);
constructed[i] = true;
}
}
} catch (...) {
niz = tmp;
destroy();
throw;
}
niz = tmp;
name = other.name + " (2)";
}
return *this;
}
T& operator[](std::size_t i)
{
if (niz && !constructed[i]) {
new(niz + i) T();
constructed[i] = true;
}
return niz[i];
}
};
struct my_struct
{
char c;
my_struct(char c = 'a')
: c(c)
{
std::cout << "my_struct('" << c << "')\n";
}
~my_struct()
{
std::cout << "~my_struct('" << c << "')\n";
}
};
int main()
{
::set<char> a, a2(3);
a.name = "a";
a2.name = "a2";
{
::set<my_struct> b(3);
b.name = "b";
b[0].c = '1';
b[2].c = '3';
b[1].c = '2';
::set<my_struct> b2(4);
b2.name = "b2";
b = b2; b.name += ", going by the name 'b'";
b[0].c = 'A';
b2[1].c = 'B';
}
}
Note: I do not recommend actually using this code. The point is to learn about the placement new operator and explicitly invoking a destructor through a pointer.
See STL templates vector and set for standard alternatives.