tags:

views:

231

answers:

5

I have some struct:

struct A
{
 const char* name_;
 A* left_;
 A* right_;
 A(const char* name):name_(name),
      left_(nullptr),
      right_(nullptr){}
 A(const A&);
 //A(const A*);//ToDo
 A& operator=(const A&);
 ~A()
 {
  /*ToDo*/
 };
};
/*Just to compile*/
A& A::operator=(const A& pattern)
{

 //check for self-assignment
 if (this != &pattern) 
 {
  void* p = new char[sizeof(A)];
 }
 return *this;
}

A::A(const A& pat)
{
 void* p = new char[sizeof(A)];
 A* tmp = new (p) A("tmp");
 tmp->~A();
 delete tmp;//I WONDER IF HERE I SHOULD USE DIFFERENT delete[]?
}

int _tmain(int argc, _TCHAR* argv[])
{
 A a("a");
 A b = a;
 cin.get();
 return 0;
}

Guys I know this is far from ideal and far from finished. But please don't tell me the answer how to do it properly. I'm trying to figure it out myself. The only think I would like to know if I'm deleting my memory in proper way.
Thanks. This is the link to different question which is really important to me

+4  A: 
void* p = new char[sizeof(A)];
A* tmp = new (p) A("tmp");
tmp->~A();
delete tmp;//I WONDER IF HERE I SHOULD USE DIFFERENT delete[]?

No. You have already called the destructor so it is not correct to call delete which will cause another destructor call. You only need to free the memory. e.g.

delete[] static_cast<char*>(p);

If you are allocating raw memory for use with placement new it is more conventional to directly use an allocation function. e.g.

void* p = ::operator new[](sizeof(A));
A* tmp = new (p) A("tmp");
tmp->~A();
::operator delete[](p);

Consider doing something simpler, though. This block could be replaced with a single local variable which would be more robust.

A tmp("tmp");
Charles Bailey
Since `new[]` invokes `::operator new[](size_t)` you should use `::operator delete[](void*)`.
Kirill V. Lyadvinsky
@Kirill: Oh yes, thanks.
Charles Bailey
@Kirill: Is this even guaranteed? Surely since `p` was allocated with `new[]`, the only truly defined way to deallocate it would be `delete[] p` (after casting back to `char*`). For example, the compiler might use `operator new[]` with `new[]`, but with a greater size (for whatever reason) and return an offsetted pointer. Then calling `operator delete[]` results in undefined behavior. I don't suspect that ever happens in practice, though, but if the above is allowed then it won't be defined to use `operator delete[]` on a pointer with a value from `new[]`.
GMan
@GMan: Good point, editing (again!).
Charles Bailey
@GMan, you're right. Result of `new[]` could be offsetted. The only guaranteed deallocation function for `new[]` is `delete[]`.
Kirill V. Lyadvinsky
And in this case it only works because `char` is a POD-type. If you allocated an array of something with a non-trivial destructor with `new[]` it would not be possible to deallocate the memory if you reused the storage for a different object.
Charles Bailey
@Kirill (Charles, you're implied in these by the way since it's your answer. Just wanted to clarify I'm not ignoring you. :P <3) Alright, thought so. Also +1 for recommending going straight to the allocation functions.
GMan
@GMan: I *know* that gcc only offsets `operator new[]` in `new[]` when the type in question has a user-declared destructor. Other implementations may be vary...
Charles Bailey
@Charles: Huh, I wonder what for. Do you know?
GMan
For the object count so that it knows how many destructors to call in `delete[] arr` . Not needed if there are no destructors to call.
Charles Bailey
I've read somewhere that if you create object with placement new you should call it's destructor manually. It was in one of the books by... can't remember his name now, he works for Microsoft now, he wrote books from c++ in depth series
There is nothing we can do
@Charles: Ah, of course. Thanks. @atch: You're correct, if you use placement new, you should explicitly call the destructor when you're done. The four-lined code block in this answer is the correct way. (Fun fact: The standard doesn't *require* you actually call the destructor on a placed-new object, even if the destructor is non-trivial; you can complete omit it. Obviously, this isn't very helpful to classes expecting to do cleanup in the destructor, so do it anyway. :P)
GMan
Guys could you please check the link I've added to my original post and tell me if my tutor is a nit picking idiot or I have still long way to learn.
There is nothing we can do
+2  A: 

If you allocate the memory with p = new[…], then you should deallocate with delete[] p[1], no exceptions.

Don't mess with tmp after it's dead.

(Placement new doesn't allocate memory, the destructor won't modify the new char[sizeof(A)], so they don't enter to the question.)

[1]: You should declare p as a char*. Or cast p to a char* in delete[].

KennyTM
But `delete[] tmp` would be wrong in this case. I think this needs more explanation. The lifetime of the array of `char` s ends once their storage is reused for the `A` object.
Charles Bailey
@Charles: I disagree. If `tmp` is allocated via `malloc`, then I shouldn't call `free`? If `tmp` is allocated via `alloca` then the stack will explode? The underlying allocation mechanism should be independent of the construction/deconstruction which doesn't affect allocation/deallocation.
KennyTM
Yes, but when you say "use `delete[]` " you don't explicitly say what to `delete[]`. Both `delete[] tmp` and `delete[] a` would cause undefined behaviour. You have to explicitly cast one of the pointers to be correct: e.g `delete[] static_cast<char*>(p)` .
Charles Bailey
@Charles: I see. I've updated the answer to clarify that.
KennyTM
A: 

To allocate memory for instance of A just write A* p = new A("tmp");. It will allocate memory and invoke constructor. Then use delete p; to invoke destructor and to deallocate memory. I can't see any need for using placement form of new in your case.

Kirill V. Lyadvinsky
A: 
A::A(const A& pat)
{
 void* p = new char[sizeof(A)];
 A* tmp = new (p) A("tmp");
 tmp->~A();
 delete tmp;//I WONDER IF HERE I SHOULD USE DIFFERENT delete[]?
}

Yes, you should use delete[], since you created the memory with new[]. Furthermore, before you reach the constructor (in this case a copy constructor) the memory has already been allocated, so there's no need to allocate it again, which is what it appears you are trying to do here.

When you delete an object, the destructor (~A in this case) is called automatically, so there is no need to explicitly call it, unless you are using a placement new. So there is no need in the delete to explicitly remove the memory allocated for the object itself, only for the members that it owns.

The copy constructor should simply copy the important information out of the thing being copied. Last, the code here takes a string: A a("a");, so you'll need a string constructor to be able to make that call:

A::A(const std::string& name)
{
   //Do stuff
}
Joel
I've read somewhere that if you create object with placement new you should call it's destructor manually. It was in one of the books by... can't remember his name now, he works for Microsoft now, he wrote books from c++ in depth series.
There is nothing we can do
I sit corrected. http://www.parashift.com/c++-faq-lite/dtors.htmlHowever, placement new still seems an odd choice here.
Joel
A: 

If you are allocating memory for a pointer in the class, the destructor should deallocate (delete) the memory also:

class Node
{
  char * name_;
  Node * p_left;
  Node * p_right;

  Node(const char * new_name)
  : name_(NULL), p_left(NULL), p_right(NULL)
  {
    size_t size = strlen(new_name);
    name_ = new char [size + 1]; // + 1 for terminating null
  }
  ~Node()
  {
     delete[] name_;
  }
};

I highly suggest:

  1. Using std::string instead of char * for text.
  2. Move the links into a base class (preference is to use template for the data section of the Node.}

The base class will allow you to adapt the node to different data types:

struct Node
{
    Node * p_left;
    Node * p_right;
};

struct Name_Node
: public Node
{
  std::string name;
};

struct Integer_Node
: public Node
{
  int value;
};

OTOH, you may want to use std::map or std::list after this exercise.

Thomas Matthews