I am in a rather specific situation that requires me to use a vector of raw pointers as a class member:
I need to keep a list of abstract objects (I understood I could not use vector<AbstractClass> directly).
I can't use Boost or std::tr1, so no smart pointer library (because of constraints from the hardware I'm using).
Here is a basic example of what I need:
#include <vector>
class Foo
{
virtual void foo() {}
};
class Bar : public Foo
{
void foo(){}
};
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
};
Coming from Java, I am very scared of pointers and memory leaks. This post initially suggests to delete the objects manually when the object goes out of scope. Is it enough to do this in the destructor of FooBar?
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
~FooBar(){
for(vector<Foo*>::iterator it=list.begin(); it!=list.end(); it++)
delete *it;
}
};
By the rule of three, I guess I also have to implement a copy constructor and an assignment operator. Is the following (based on this and that posts) a correct implementation ?
FooBar::FooBar(const FooBar& orig) : list(orig.list.size()) {
try {
vector<Foo*>::iterator thisit = list.begin();
vector<Foo*>::const_iterator thatit = orig.list.cbegin();
for (; thatit != orig.list.cend(); ++thisit, ++thatit)
*thisit = *thatit; // I'm okay with a shallow copy
} catch (...) {
for (vector<Foo*>::iterator i = list.begin(); i != list.end(); ++i)
if (!*i)
break;
else
delete *i;
throw;
}
}
FooBar& operator=(const FooBar& orig){
FooBar tmp(orig);
swap(tmp);
return *this;
}
Your code has some major flaws as far as I can tell. First of all, your first code is correct, and yes, your destructor does what it should do, as long as your class owns the objects and doesn't get copied.
Also, your copy constructor and assignment operator looks problematic. First of all, if you are okay with shallow copying, you don't even have to write the functions anyway, std::vector's copy constructor and assignment operators do what you have done manually anyway. I don't think you need that try-catch block. By the way, where is the implementation for that swap in your assignment operator?
For your purposes, a very small reference counting scheme would work:
class FooPtr
{
Foo* _raw;
size_t* _ctl;
FooPtr(Foo* ptr) :
_raw(ptr), _ctl(new size_t(0))
{
}
template <class T>
FooPtr(T* ptr) : FooPtr(static_cast<Foo*>(ptr)) {}
FooPtr(const FooPtr& rhs) :
_raw(rhs._raw), _ctl(rhs._ctl)
{
++(*_ctl);
}
~FooPtr()
{
if (_raw == nullptr) return;
--(*_ctl);
if (*_ctl == 0)
{
delete _raw;
delete _ctl;
}
}
FooPtr& operator=(FooPtr ptr)
{
std::swap(*this, ptr);
return *this;
}
Foo* operator->()
{
return _raw;
}
Foo& operator*()
{
return *_raw;
}
}
Your class now looks like this, no destructor, copy ctor or assignment operator needed:
class FooBar
{
vector<FooPtr> list;
void add(){ list.emplace_back(new Bar()); }
}
I've written this quickly, it might contain bugs though it gives you the basic idea I suppose. Also, I've used some c++11 features like emplace_back and nullptr but they can easily be converted in non 11 code.
I would write a small wrapper around a vector that frees the elements for you:
template<class Container>
void delete_elements(Container& cont) {
typedef typename Container::reverse_iterator iterator;
iterator end = container.rend();
for (iterator i = container.rbegin(); end != i; ++i) {
delete *i;
}
}
struct deep_copy {
template<typename T>
T* operator()(T const* const other) {
return other->clone();
}
}
template<typename T>
struct ptr_vector {
std::vector<T*> container;
ptr_vector() { }
ptr_vector(ptr_vector const& other) {
std::vector<T*> tmp;
tmp.reserve(other.container.size());
try {
std::transform(other.container.begin(), other.container.end(),
std::back_inserter(tmp), deep_copy());
}
catch (...) {
(delete_elements)(tmp);
throw;
}
container.swap(tmp);
}
ptr_vector& operator=(ptr_vector other) {
container.swap(other.container);
return *this;
}
~ptr_vector() {
(delete_elements)(container);
}
};
Now you can just use a ptr_vector<Foo> v and use v.container to add elements. Note: you should add a clone member function to your base class so you don't slice when you want to copy.
Related
I study for an exam in c++
and I was asked if in this code the d'tor of the class should use delete[] instead delete:
template <class T>
class ClonePtr
{
private:
T* ptr;
public:
explicit ClonePtr(T* p = nullptr) : ptr(p) {}
~ClonePtr() { if(ptr!=nullptr) delete []ptr; }
ClonePtr(const ClonePtr& other) : ptr(nullptr)
{
*this = other;
}
ClonePtr(ClonePtr&& other) : ptr(other.ptr)
{
other.ptr = nullptr;
}
ClonePtr& operator=(const ClonePtr& other)
{
if (this != &other)
{
delete ptr;
if (other.ptr != nullptr)
ptr = other.ptr->clone();
}
return *this;
}
T& operator*() const { return *ptr; }
};
The right answer to the question is yes,
but why is that?
+I have two more little question regarding this code:
It says in the exam that type T must be a class and cannot be a primitve type.
Why is that?
On the other hand, I tried writing this code:
class A
{
private:
int x;
public:
A(int x = 8) : x(x) { };
};
int main()
{
ClonePtr<int> h(new A(7));
}
and got a compiler error: C2664 "cannot convert argument 1 from A* to T*"
I will very much appreciate your help.
thanks :)
There is no way for us to answer this definitely one way or the other, because we can't see what kind of pointer is being passed to ClonePtr(T*), or what kind of pointer is being returned by clone(). This class is not doing any memory allocations of its own.
IF both pointers are being allocated with new and not by new[], then delete would be the correct answer, not delete[].
IF both pointers are being allocated with new[] and not by new, then delete[] would be the correct answer, not delete.
For that matter, since this code is taking ownership of memory allocated by something else, and it is clear by this class' use of nullptr and a move constructor (but not a move assignment operator!) that you are using C++11 or later, so you should be utilizing proper ownership semantics via std::unique_ptr or std::shared_ptr, not using a raw pointer at all.
Especially since your copy assignment operator is not setting ptr to nullptr after delete ptr; if other.ptr is nullptr, leaving *this in a bad state. Using proper ownership semantics would have avoided that.
Try this instead:
UPDATE: now you have posted additional code (that doesn't compile, since an A* can't be assigned to an int*, and A does not implement clone()) showing ptr being set to an object allocated with new, so the correct answer is:
delete MUST be used, not delete[].
So the "right answer" to your exam is wrong.
But proper ownership semantics would be even better, eg:
#include <memory>
template <class T>
class ClonePtr
{
private:
std::unique_ptr<T> ptr;
public:
ClonePtr(std::unique_ptr<T> p) : ptr(std::move(p)) {}
ClonePtr& operator=(ClonePtr other)
{
ptr = std::move(other.ptr);
return *this;
}
T& operator*() const { return *ptr; }
};
class A
{
private:
int x;
public:
A(int x = 8) : x(x) { };
std::unique_ptr<A> clone() const
{
return std::make_unique<A>(x);
// or: prior to C++14:
// return std::unique_ptr<A>(new A(x));
}
};
int main()
{
ClonePtr<A> h(std::make_unique<A>(7));
// or, prior to C++14:
// ClonePtr<A> h(std::unique_ptr<A>(new A(7)));
}
I am trying to implement a smart pointer (basically unique pointer) in C++ using templates for my understanding.
This is what I have coded
using namespace std;
template<typename T>
class smartPointer
{
private:
T *mPtr;
public:
smartPointer(T* init=nullptr):mPtr(init)
{
cout<<"Inside ctr"<<endl;
}
//silence the default ctr
smartPointer() = delete;
//disable copy ctr and copy assignment
smartPointer (const smartPointer& other) = delete;
smartPointer& operator=(const smartPointer& other) = delete;
//implement move ctr and move assignment
smartPointer (smartPointer&& other)
{
cout<<"Inside move ctr "<<endl;
mPtr = other.mPtr;
other.mPtr = nullptr;
}
smartPointer& operator=(smartPointer&& other)
{
cout<<"Inside move = before "<<endl;
if(&other != this)
{
mPtr = other.mPtr;
other.mPtr = nullptr;
cout<<"Inside move = after "<<endl;
}
return *this;
}
//deference
T& operator*()
{
return *mPtr;
}
//arrow access operator
T* operator->()
{
return mPtr;
}
~smartPointer()
{
cout<<"Inside ~dtr"<<endl;
if(mPtr != nullptr)
delete mPtr;
}
};
int main()
{
smartPointer<int> sptr(new int);
// Even smartPointer<int> sptr(new int[20]); too works
*sptr = 10;
cout<<"Value pointed by the pointer is "<<*sptr<<endl;
smartPointer<int> sptr2(new int);
// sptr2 = sptr; // complains as expected
sptr2 = move(sptr); // works well
/*
How to
smartPointer<int[]> sptr(new int[20]);
*/
return 0;
}
How to make this smart pointer to work for smartPointer ?
The build command I give is
g++ -std=c++11 -smartPointer.cpp -o smartPointer
You can add operator[] to the base template, and use SFINAE to ensure it only compiles when the type is an array type. You would do the same to make sure operaotr* and operaotr-> only compile for the non-array version. Then, tag dispatch can be used to call the correct delete expression.
template<typename T>
class smartPointer
{
private:
T *mPtr;
del_member(std::true_type) { delete[] mPtr; }
del_member(std::false_type) { delete mPtr; }
public:
// Removed you code for brevity.
//deference
auto operator*() -> std::enable_if_t<!std::is_array<T>::value, T&>
{
return *mPtr;
}
//arrow access operator
auto operator->() -> std::enable_if_t<!std::is_array<T>::value, T*>
{
return mPtr;
}
auto operator[](std::size_t idx) -> std::enable_if_t<std::is_array<T>::value, T&>
{
return mPtr[idx];
}
~smartPointer()
{
del_member(std::is_array<T>{});
}
};
The above is perfectly legal, and can exist in the same template. Remember the members of a template are instantiated only when used. So unless users of your class try to use an operator which the underlying pointer shouldn't support, they will get no errors.
If your compiler doesn't support c++14 type traits yet, the above trailing return types can be turned to the form:
typename std::enable_if<CONDITION, TYPE>::type
For your c'tor, I recommend you don't hard-code T*. Instead add the following type alias to your class, and change the c'tor accordingly.
using ptr_type = std::conditional_t<std::is_array<T>::value, std::decayt_t<T>, std::add_pointer_t<T>>;
smartPointer(ptr_type init)
despite the ocean of smart pointer questions out there, I seem to be stuck with one more. I am trying to implement a ref counted smart pointer, but when I try it in the following case, the ref count is wrong. The comments are what I think should be the correct ref counts.
Sptr<B> bp1(new B); // obj1: ref count = 1
Sptr<B> bp2 = bp1; // obj1: ref count = 2
bp2 = new B; // obj1: ref count = 1, obj2: rec count = 1 **problem**
In my implementation, my obj2 ref count is 2, because of this code:
protected:
void retain() {
++(*_rc);
std::cout << "retained, rc: " << *_rc << std::endl;
}
void release() {
--(*_rc);
std::cout << "released, rc: " << *_rc << std::endl;
if (*_rc == 0) {
std::cout << "rc = 0, deleting obj" << std::endl;
delete _ptr;
_ptr = 0;
delete _rc;
_rc = 0;
}
}
private:
T *_ptr;
int *_rc;
// Delegate private copy constructor
template <typename U>
Sptr(const Sptr<U> *p) : _ptr(p->get()), _rc(p->rc()) {
if (p->get() != 0) retain();
}
// Delegate private assignment operator
template <typename U>
Sptr<T> &operator=(const Sptr<U> *p) {
if (_ptr != 0) release();
_ptr = p->get();
_rc = p->rc();
if (_ptr != 0) retain();
return *this;
}
public:
Sptr() : _ptr(0) {}
template <typename U>
Sptr(U *p) : _ptr(p) { _rc = new int(1); }
// Normal and template copy constructors both delegate to private
Sptr(const Sptr &o) : Sptr(&o) {
std::cout << "non-templated copy ctor" << std::endl;
}
template <typename U>
Sptr(const Sptr<U> &o) : Sptr(&o) {
std::cout << "templated copy ctor" << std::endl;
}
// Normal and template assignment operator
Sptr &operator=(const Sptr &o) {
std::cout << "non-templated assignment operator" << std::endl;
return operator=(&o);
}
template <typename U>
Sptr<T> &operator=(const Sptr<U> &o) {
std::cout << "templated assignment operator" << std::endl;
return operator=(&o);
}
// Assignment operator for assigning to void or 0
void operator=(int) {
if (_ptr != 0) release();
_ptr = 0;
_rc = 0;
}
The constructor is initialized with a ref count = 1, but in my assignment operator, I am retaining the object, making the ref count = 2. But it should only be 1 in this case, because bp2 = new B is only one pointer to that object. I've looked over various smart pointer implementation examples, and I can't seem to figure out how they deal with this case that I'm having trouble with.
Thanks for your time!
The assignment operator is riddled with small errors and unnecessary complex anyway. For example, when you assign Sptr<T> to itself it will have funny effects. Most manually written assignment operators should look like this:
T& T::operator= (T const& other) {
T(other).swap(*this);
return *this;
}
... or
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
Once stateful allocators enter the game things change a bit but we can ignore this detail here. The main idea is to leverage the existing work done for the copy constructor and the destructor. Note, that this approach also works if the right hand side isn't T, i.e., you can still leverage a corresponding constructor, the destructor, and swap(). The only potentially additional work is desirable anyway, and trivial to implement: the swap() member. In your case that is very simple, too:
template <typename T>
void Sptr<T>::swap(Sptr<T>& other) {
std::swap(this->_ptr, other._ptr);
std::swap(this->_rc, other._rc);
}
Just a note on your assignment operator taking an int: This is a very bad idea! To reset the pointer, you should probably better have a reset() method. In C++ 2011 you could reasonably have a method taking a std::nullptr_t for this purpose, though.
Define an assignment operator for the raw pointer type. Otherwise, it will construct a new smart pointer with ref count 1, which you will then increase to 2.
Example:
template <typename U>
Sptr<T> &operator=(U *p)
{
if (_ptr != 0)
{
_ptr = p;
_rc = new int(1);
}
return *this;
}
This stuff is notoriously tricky, and there are probably more bugs waiting. I would make the constructor explicit to prevent other unintentional constructions.
From what I see of your code, you need a proper destructor in your smart pointer class to call release and fix the counter. You need at least that modification for your counter to work correctly.
I didn't see a Sptr(T *) constructor in your code, did you omit it?
As a side note, I would probably use a std::pair to store the counter and the T pointer, but your data layout works as it is. As another answer pointed it out, you should pay attention to the self assignment case though.
Here's a minimal implementation following your data layout and interface choices:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Sptr {
protected:
T *_ptr;
int *_rc;
virtual void retain() {
if (_rc) // pointing to something
++(*_rc);
clog << "retain : " << *_rc << endl;
}
virtual void release() {
if (_rc) {
--(*_rc);
clog << "release : " << *_rc << endl;
if (*_rc == 0) {
delete _ptr;
_ptr = NULL;
delete _rc;
_rc = NULL;
}
}
}
public:
Sptr() : _ptr(NULL), _rc(NULL) {} // no reference
virtual ~Sptr() { release(); } // drop the reference held by this
Sptr(T *p): _ptr(p) { // new independent pointer
_rc = new int(0);
retain();
}
virtual Sptr<T> &operator=(T *p) {
release();
_ptr = p;
_rc = new int(0);
retain();
return *this;
}
Sptr(Sptr<T> &o) : _ptr(o._ptr), _rc(o._rc) {
retain();
}
virtual Sptr<T> &operator=(Sptr<T> &o) {
if (_rc != o._rc){ // different shared pointer
release();
_ptr = o._ptr;
_rc = o._rc;
retain();
}
return *this;
}
};
int main(){
int *i = new int(5);
Sptr<int> sptr1(i);
Sptr<int> sptr2(i);
Sptr<int> sptr3;
sptr1 = sptr1;
sptr2 = sptr1;
sptr3 = sptr1;
}
_rc is the indicator in your class that a pointer is shared with another instance or not.
For instance, in this code:
B *b = new B;
Sptr<B> sptr1(b);
Sptr<B> sptr2(b);
sptr1 and sptr2 are not sharing the pointer, because assignments were done separately, and thus the _rc should be different, which is effectively the case in my small implementation.
For training purposes, I am trying to write my own smartpointer, imitating std::shared_ptr. I have a static std::map<void *, int> ref_track that keeps track whether there are still shared pointer referencing a certain block in memory.
My concept is this:
template <typename PType>
class shared_ptr
{
public:
shared_ptr()
: value_(nullptr), ptr_(nullptr)
{}
template <typename T>
explicit shared_ptr(T * ptr)
: shared_ptr()
{
reset(ptr);
}
template <typename T>
shared_ptr(shared_ptr<T> const & other)
: shared_ptr()
{
reset(other.get());
}
~shared_ptr()
{
reset();
}
void reset()
{
if(value_)
{
delete value_; // Segmentation fault here!
value_ = 0;
ptr_ = 0;
}
}
template <typename T>
void reset(T * ptr)
{
reset();
if(ptr)
{
value_ = new shared_ptr_internal::storage_impl<
T
>(ptr);
ptr_ = ptr;
}
}
PType * get() const
{
return ptr_;
}
typename shared_ptr_internal::ptr_trait<PType>::type operator *()
{
return *ptr_;
}
private:
shared_ptr_internal::storage_base * value_;
PType * ptr_;
};
When running my test suite, I noticed that
shared_ptr<int> a(new int(42));
a.reset(new int(13));
works fine, but
shared_ptr<int> a(new int(42));
a = shared_ptr<int>(new int(13));
leads to problems: *a is 0 instead of 13, and delete value_ crashes with a segmentation fault in the destructor of a. I have marked the crash in the source code with a comment.
The used internal classes are
namespace shared_ptr_internal
{
typedef std::map<void *, int> ref_tracker;
typedef std::map<void *, int>::iterator ref_tracker_iterator;
typedef std::pair<void *, int> ref_tracker_entry;
static ref_tracker ref_track;
struct storage_base
{
virtual ~storage_base() {}
};
template <typename PType>
struct storage_impl : storage_base
{
storage_impl(PType * ptr)
: ptr_(ptr)
{
ref_tracker_iterator pos = ref_track.find(ptr);
if(pos == ref_track.end())
{
ref_track.insert(
ref_tracker_entry(ptr, 1)
);
}
else
{
++pos->second;
}
}
~storage_impl()
{
ref_tracker_iterator pos = ref_track.find(ptr_);
if(pos->second == 1)
{
ref_track.erase(pos);
delete ptr_;
}
else
{
--pos->second;
}
}
private:
PType * ptr_;
};
template <typename PType>
struct ptr_trait
{
typedef PType & type;
};
template <>
struct ptr_trait<void>
{
typedef void type;
};
}
Sorry for the bulk of source code, but I really do not know how I could narrow it down further. I would be grateful for any ideas what could be causing the segfault, and moreover why this does not happen when using reset manually.
Update
My (not-working) assignment operator:
template <typename T>
shared_ptr<PType> & operator =(shared_ptr<T> const & other)
{
if(this != &other)
{
value_ = nullptr;
ptr_ = nullptr;
reset(other.get());
}
return *this;
}
You're missing an assignment operator.
This means that in the following code:
a = shared_ptr<int>(new int(13));
a temporary shared pointer is created; then the default assignment operator simply copies the pointer to a without releasing the old value or updating the reference count; then the temporary deletes the value, leaving a with a dangling pointer.
Seems like a violation of the rule of three: You have a custom copy constructor and a custom destructor, but no custom assignment operator. Therefore a = shared_ptr<int>(new int(13)) will just copy the two pointers value_ and ptr_ from the temporary, without any update of your reference tracking. Therefore when you destroy the temporary, there are no more tracked references to that pointer, which will lead to its deletion. Also note that the old pointer will have been leaked in the assignment.
you forgot to add an assignment operator to your pointer class that should decrement the number references to the old object and increment the number of references to the assigned object. Most times it's the easiest way to implement operator= in terms of a copy d'tor and a swap function:
shared_ptr& shared_ptr<T>::operator=( shared_ptr<T> other )
{
other.swap( this );
return *this;
}
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.