EDIT: I had to use unique_ptr or follow the rule-of-five I'm still learning about it so I used unique_ptr and it worked. But I have a question, the destructor now is being called twice and I think that is no problem as long as the block of memory that the pointer is pointing to is not being freed twice, Right??
I made a simple "String" class in c++ (Know it from The Cherno). So I made it and it seems to work well until I decided to add an empty constructor to be able to initialize it with no parameter but when I did that the destructor is being called twice.
// here is the header file
#pragma once
#include <iostream>
using namespace std;
class String
{
private:
//Hold the raw of chars in the heap memory
char* m_Buffer;
//The size of the buffer in heap
unsigned int m_Size;
public:
//An empty Constructor
String()
: m_Buffer(nullptr), m_Size(0)
{
cout << "created Empty." << endl;
}
// A Constructor
String(const char* string)
{
m_Size = strlen(string);
m_Buffer = new char[m_Size + 1];
memcpy(m_Buffer, string, m_Size + 1);
m_Buffer[m_Size] = 0;
}
// A destructor
~String()
{
cout << "Destroy!!" << endl;
delete[] m_Buffer;
}
// Function resposable for coping
String(const String& other)
: m_Size(other.m_Size)
{
m_Buffer = new char[m_Size + 1];
memcpy(m_Buffer, other.m_Buffer, m_Size + 1);
}
char& operator[](unsigned int& index)
{
return m_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& stream, const String& other);
};
std::ostream& operator<<(std::ostream& stream, const String& other)
{
stream << other.m_Buffer << endl;
return stream;
}
//here is the main file
#include "LclString.h"
int main()
{
String a = "asdc";
a = "ads"; // The Destructor is being called here the first time
cin.get();
} // and here is the second time
A piece of advice will be appreciated
......................................
Your destructor calls delete[] m_Buffer. A pointer may never be deleted twice. If you do delete a pointer twice, then the behaviour of the program will be undefined. You must avoid ever doing that. To achieve avoiding that, you must make sure that no two instances of the class have the same pointer value in the member.
Consider what the implicitly generated assignment operator of your class does. It copies all members from the right hand operand to the left hand operand. Can you see why that is a problem? All members include the m_Buffer pointer. The assignment operator will cause two instances of the class to have the same pointer. And when the second instance is destroyed, it deletes that same pointer again. And the behaviour of the program is undefined.
There is another related problem, the implicit assignment operator overwrites the old m_Buffer. Who's going to delete the pointer that was overwritten? No-one is going to delete it because the value was lost by the assignment. That's a memory leak.
Conclusion:
Avoid using owning bare pointers.
Avoid using new and delete directly.
If you have a user defined destructor, then you probalby also need user defined copy/move constructor and assignment operator. This is known as rule of 5 (previously rule of 3). If you use smart pointers instead of owning bare pointers, then you usually don't need a user defined destructor.
Related
Here is the code:
#include <iostream>
class String
{
public:
String() = default;
String(const char* string)
{
printf("created\n");
size = strlen(string);
data = new char[size];
memcpy(data, string, size);
}
~String()
{
delete data;
}
private:
char* data = nullptr;
size_t size = NULL;
};
class Entity
{
public:
Entity(String name) :
name(name)
{
}
private:
String name;
};
int main()
{
Entity entity("name");
}
It triggers a break-point in file delete_scalar.cpp
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK);
#else
free(block);
#endif //A cross sign here and says "learn.exe has triggered a breakpoint"
}
I copied the code from a video I was watching. He says the code does not work because a copy constructor is missing and goes on to write a copy constructor but does not explain why It does not work if the copy constructor is missing. I want to know exactly which part of the code triggers this breakpoint and why does the default copy constructor not suffice?
Why does default copy constructor not work for this class
Because of this:
~String()
{
delete data; // sic (should be delete[] data)
}
If you delete a pointer, then all pointers to that object become invalid. If you delete an invalid pointer, then the behaviour of the program is undefined.
If there is an instance of this class whose member is a copy of the member of another instance of that class, then destroying one instance will cause the destructor of the other instance to have undefined behaviour.
Undefined behaviour must be avoided. To avoid the case described above, you must establish a class invariant (a post condition that applies to all member functions) that no two instances of the class ever have the same value for the member.
The default copy constructor copies all members. This violates the class invariant described above and leads the the undefined behaviour that was described above that.
Same applies to the move constructor and the cope and move assignment operator. This is why the "rule of 5" exists.
size_t size = NULL;
This is potentially ill-formed. NULL is a macro that expands to a null pointer constant. Some null pointer constants (nullptr in particular) are not convertible to std::size_t.
Don't use NULL for anything other than pointers. In fact, don't use NULL at all because it has been entirely obsoleted by nullptr.
P.S. data = new char[size];, delete data; this also causes the behaviour of the program to be undefined. delete[] must be used instead when an array has been allocated.
P.P.S. I recommend avoiding bare owning pointers. std::unique_ptr can be used to simplify this class. Of course, this class doesn't do anything useful that std::string doesn't, so it shouldn't be used for any other purpose than to learn how to write it.
I am pretty new to the C++ world...
I have a question about destructors in C++.
If there is a class like:
class TestClass {
public:
double *array;
}
When I put a pointer of one array to *array and put again another and delete Testclass instance, then I can no longer access the older one?
It becomes an error:
int main() {
TestClass tc;
tc.array = new double[10];
double *array2 = tc.array;
tc.array[3]=9;
tc.array = new double[10];
delete &tc; // <------------------- this one.
std::cout<<array2[3]<<std::endl;
// std::cout<<tc.array[3]<<array2[3]<<std::endl;
}
If there is no delete &tc and activate the commentline (the last line),
'09' shows up, (tc.array[3] != array2[3] => tc.array != array2)
which does not mean tc.arrray is not the one in *array2 ??
What is wrong?
The tc is a function-scoped variable (allocated on the stack). It's not allocated with new. You can't delete it. it will be freed automatically when the function return
Dealing with raw pointers is somewhat tricky and your class would probably benefit from using a std::vector<double> instead of a raw pointer. Another choice could be a std::unique_ptr<double[]> that handles deleting the pointer for you.
That said, if you want to try it out, you should know when and what to delete.
tc is an automatic variable. You didn't create tc using new so you should not delete it. It will be destroyed when it goes out of scope - when the program leaves the block in which tc was created.
You haven't defined a destructor for TestClass so the memory to which array points will still be allocated even after tc is destroyed. The destructor would typically look like this:
~TestClass() {
delete[] array;
}
You also assign directly to tcs internal pointer, which is dangerous. You should have just as many delete[] as you have new[]s, which is not the case in your program, so it'll leak. You should typically hide the internal pointer so it can't be reassigned without the object owning it having control.
For manual memory management to work properly, there are some member functions you should consider:
Copy constructor
Move constructor
Copy assignment operator
Move assignment operator
Destructor
You can read more about those here: The rule of three/five/zero
Here's an example of what your class could look like with the 5 member functions mentioned above plus a default constructor, a converting constructor taking a pointer as parameter and two subscript operators.
#include <iostream>
#include <utility> // std::move, std::swap, std::exchange
class TestClass {
public:
// default constructor
TestClass() :
array(nullptr) // member initializer
{
std::cout << "default ctor\n";
}
// converting constructor - refuse lvalue pointers since we want to make sure
// that we "own" the pointer
TestClass(double*&& p) : array(p) {
std::cout << "converting ctor " << p << "\n";
}
// copy constructing without knowledge about the size of the array isn't possible
TestClass(const TestClass&) = delete;
// move construction works though
TestClass(TestClass&& rhs) :
array(std::exchange(rhs.array, nullptr)) // steal pointer from rhs
{
std::cout << "move ctor\n";
}
// copy assignment without knowledge about the size of the array isn't possible
TestClass& operator=(const TestClass&) = delete;
// move assignment works though
TestClass& operator=(TestClass&& rhs) {
std::cout << "move assignment\n";
// swap pointers with rhs - let rhs delete[] our current pointer
std::swap(array, rhs.array);
return *this;
}
~TestClass() { // destructor
delete[] array;
}
// subscripting support
double& operator[](size_t idx) { return array[idx]; }
double operator[](size_t idx) const { return array[idx]; }
private: // hide your raw pointer from direct access
double* array;
};
void printer(const TestClass& tc, size_t idx) {
// use const version of operator[] in the class
std::cout << tc[idx] << "\n";
}
int main() {
TestClass tc; // default ctor
// use the converting constructor that creates a TestClass object from a pointer.
// It is then move assigned to tc
tc = new double[10];
tc[3] = 1; // use the non-const version of operator[] in the class
printer(tc, 3);
double* a = new double[10];
// tc = a; // this won't work since we don't accept lvalues in assignment
tc = std::move(a); // make an xvalue to allow the assignment. "a" should not be
// delete[]-ed after this since we granted tc the possibillity
// to take ownership the pointer.
tc[3] = 2;
printer(tc, 3);
}
Our professor posted a custom 'String' template file online, and asked us a while ago to fill out the functions below. My question, in order to try and understand this, is why the top three constructors have Text = NULL; and below it, this = source;, some other form of it. I feel like each should say Text = the_input_parameter.
Many thanks, here's the code:
class String
{
public:
// Default constructor
String()
{
Text = NULL;
}
String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
~String()
{
delete[] Text;
}
// Assignment operator to perform deep copy
String& operator = (const char* text)
{
// Ddispose of old Text
delete[] Text;
// +1 accounts for NULL-terminator
int trueLength = GetLength(text) + 1;
// Dynamically allocate characters on heap
Text = new char[trueLength];
// Copy all characters from source to Text; +1 accounts for NULL-terminator
for ( int i = 0; i < trueLength; i++ )
Text[i] = text[i];
return *this;
}
// Returns a reference to a single character from this String
char& operator [] (int index) const
{
int length = GetLength();
// Check for valid index
if ( (index < 0) || (index > length) )
{
stringstream error;
error << "operator[] - index " << index << " is out of bounds (0.." << (length - 1) << ")";
throw String(error.str().c_str());
}
return Text[index];
}
private:
// The encapsulated C-string
char* Text;
};
Why you should not implement constructors in terms of assignment:
It gets pretty nasty in derived classes. Think about it.
It's difficult to make exception safe.
It's also inefficient, to boot (requiring default construction then assignment).
So the answer to why it’s done that way in your example code, is possibly that your professor doesn’t know diddly about C++ programming.
Otherwise, it's difficult to say: it just doesn't make any sense at all to do that.
Going the other way, however, namely implementing copy assignment in terms of copy construction, is very common and is known as the copy-and-swap idiom.
It's simple, exceptions safe and generally efficient, and goes like this:
class Foo
{
public:
void swap_with( Foo& other ) throw()
{
// No-throwing swap here.
}
void operator=( Foo other )
{
other.swap_with( *this );
}
};
yup, that's all.
variants include naming the swapper just swap, and letting the assignment operator return a reference, and some prefer to pass the argument by reference and then make a copy (using copy construction).
It's just a way to factor out common code into a helper function. In this case, operator=() acts as the helper function. Its function is to deallocate the current string (in this case, NULL) and perform a deep copy of the right-hand side.
I feel like each should say Text = the_input_parameter.
For String(const String& source) this wouldn't compile since source isn't the right type.
For String(const char* text) this wouldn't be correct since that would just assign the pointer instead of performing a deep copy.
The above assumes you are only showing us a part of the class, and that the actual class defines an appropriate assignment operator and a destructor. If it doesn't, you need a new professor.
The class manages memory, so destructor releases it, and assignment operator allocates new for new data and releases old one (shoud be in that order).
Then the explanation of initial assignment is clear: you need to initialize the member field to a correct value, as otherwise it will contain garbage (some pointer to somewhere), which code will attempt to use and free.
Though it is not seen from the code, there might be also assignment for const String& and type cast operator operator const char *() const.
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
class myClass{
public:
int *num1;
myClass();
};
myClass::myClass(){
num1 = new int[1];
num1[0] = 10;
}
int main()
{
myClass *myclass;
myclass = new myClass[10];
cout << myclass[0].num1[0] << endl;
delete &myclass[0];
cout << myclass[0].num1[0] << endl;
}
I want to delete the first instance of myclass (myclass[0]).
This code does not run correctly, it fails during the delete part. There is probably something I am missing.
What did I do wrong?
You cannot delete just a portion of an array created with new. new allocates a block of memory which can only be deleteed all together.
If you want an object to free its own internal data you'll have to arrange for the class, which should encapsulate and hide its own internal resources, to do that itself.
If you want a smaller block of memory holding the array you allocated, then you must allocate a smaller block and move the contents that you wish to keep into the new block, and then delete the entire old block:
int *arr = new int[10];
int *tmp = new int[9];
std::copy(arr+1, arr+10, tmp);
delete [] arr;
arr = tmp;
You need to design your class to manage its own resources, and to handle copying or moving. Your current myClass allocates an array but relies on other code to handle cleaning up. This is not a good way to go about doing this, because often no other code is in a good position to do the correct thing, and even when you could you'll very frequently make mistakes.
Since you're allocating in the constructor you need a destructor that handles deallocation. And then since you implement one of three special operations (copy-ctor, copy-assignment, destructor) you need to consider implementing them all. (This is called 'The Rule of Three'. In C++11 it becomes 'The Rule of Five' with the addition of move-ctors and move assignment.)
class myClass {
public:
myClass();
// destructor to handle destroying internal resources correctly
~myClass();
// copy constructor and copy assignment operator, to handle copying correctly
myClass(myClass const &rhs);
myClass &operator=(myClass const &rhs);
// move constructor and move assignment operator, to handle moves correctly (C++11)
myClass(myClass && rhs);
myClass &operator= (myClass &&rhs);
private:
int *num1; // private so external code can't screw it up
public:
// limited access to num1
int size() const { if (num1) return 1; else return 0; }
int &operator[] (size_t i) { return num1[i]; }
};
You can implement the constructor just as you did, or you could use the initializer list and C++11 uniform initialization:
myClass::myClass() : num1(new int[1]{10}) {}
Now, the destructor you want depends on the semantics you want the class to have, and the particular invariants you want to maintain. 'value' semantics are the norm in C++ (if you're familiar with Java or C# those languages encourage or require 'reference' semantics for user defined types). Here's a destructor you might use if you want value semantics, and if you maintain an invariant that num1 always owns memory or is null.
myClass::~myClass() { delete num1; }
Copying and moving can be handled in different ways. If you want to disallow them entirely you can say (in C++11):
myClass::myClass(myClass const &rhs) = delete;
myClass &myClass::operator=(myClass const &rhs) = delete;
myClass::myClass(myClass && rhs) = delete;
myClass &myClass::operator= (myClass &&rhs) = delete;
Or if you want to allow copying and or moving (and maintain value semantics and the invariant mentioned above) then you can implement either or both of these pairs of functions:
myClass::myClass(myClass const &rhs) : num1( rhs.size() ? new int[1]{rhs[0]} : nullptr) {}
myClass &myClass::operator=(myClass const &rhs) {
if (num1)
num1[0] = rhs[0];
}
myClass::myClass(myClass && rhs) : num1(rhs.num1) { rhs.num1 = nullptr; } // remember to maintain the invariant that num1 owns the thing it points at, and since raw pointers don't handle shared ownership only one thing can own the int, and therefore only one myClass may point at it. rhs.num1 must be made to point at something else...
myClass &myClass::operator= (myClass &&rhs) { std::swap(num1, rhs.num1); } // steal rhs.num1 and leave it to rhs to destroy our own num1 if necessary. We could also destroy it ourselves if we wanted to.
With this implementation you can now treat a myClass object the same as you would an int or any other 'value' type. You no longer need to worry about managing its internal resources; it will take care of them itself.
int main() {
std::vector<myClass> myclassvec(10);
cout << myclassvec[0][0] << '\n';
myclassvec.erase(myclassvec.begin()); // erase the first element
cout << myclassvec[0][0] << '\n'; // access the new first element (previously the second element);
}
Create a function inside of your class the handles the deletion of its private members, maybe called FreeMem(int index)
void myClass::FreeMem()
{
delete [] num1
}
But honestly, freeing memory of an object without the use of a destructor in this sort of a program is hazardous and downright bad practice. I would recommend freeing the memory in your destructor, so when the object terminates it frees the memory,
myClass::~myClass()
{
delete [] num1;
}
Another thing to note on, if you're only creating one value in your dynamic variable, it would be easier to write it as:
int * pnum = new int;
//or in your class..
pnum = new int;
among other things, you have a lot of flaws in your program. I would recommend re-reading up on classes again.
OK, very simple String class, which holds constant strings (i.e. can't be changed once initialized), implements the copy ctor and concatenation function conc. It's that function that's giving me trouble, because I really cannot figure out why a local variable I make isn't passed normally as return value.
Code:
class String
{
public:
String(char*);
String(const String& other);
~String();
friend String conc(const String&, const String&);
friend std::ostream& operator<<(std::ostream&, const String&);
private:
const char* const str;
};
String::String(char* aStr) : str(aStr) {}
String::String(const String& other) : str(other.str) {}
std::ostream& operator<<(std::ostream& out, const String& s)
{
out<<s.str<<"\n";
return out;
}
String::~String()
{
delete str;
}
String conc(const String& s1, const String& s2)
{
int n = strlen(s1.str) + strlen(s2.str);
int i, j;
char* newS = new char[n+1];
for(i = 0; i < strlen(s1.str); ++i)
{
newS[i] = s1.str[i];
}
for(j = 0; j < strlen(s2.str); ++j)
{
newS[i+j] = s2.str[j];
}
newS[i+j] = '\0'; //newS is made correctly
String tmp(newS); // the tmp object is made correctly
return tmp; // here tmp becomes something else --- Why??
}
int main()
{
String s1("first");
String s2("SECOND");
String s3 = conc(s1, s2); //here the copy ctor should be called, right?
std::cout<<s3;
_getch();
}
As you can see in the comments, the problem is in the conc function at the end. I have made the function return a String, and not String& on purpose, given that the value it returns shouldn't be an lvalue...
Please explain & help, thanks!:)
There are memory management problems all over the place here. Each String object should own its own memory. That means that String::String(char*) needs to allocate an array of char and copy the contents of the input string; String::String(const String&) needs to allocate an array of char and copy the contents of the input string; and String::operator= needs to delete its own string (unless it's the same as the input string), allocate an array of char, and copy the contents of the input string. Finally, String::~String() should delete[] the array of char.
It is not what you think: the problem is the deletion of the temp. You call delete instead of delete[] on an array allocated with new[] - an undefined behavior.
Once you fix that error, you will have other errors related to Strings initialized with string literals: passing them to delete[] is also undefined behavior.
The root cause of the problem is that your class does not let you differentiate between the memory that you must release and the memory that you must not release. You should do it uniformly, for example by always copying the content into an array that you allocate inside the constructor.
There are several problems with your class:
Your String( char * ) constructor assumes ownership of the pointer passed to it, so if you construct the object with a string literal the destructor will try to delete it, leading to undefined behavior.
Your copy constructor assumes ownership of the string belonging to the object being copied, instead of making its own copy, due to which the string will be double deleted.
In the conc function you allocate memory using new[] but then you delete it instead of delete[] leading to undefined behavior
The member variable str is supposed to be a char array, so the destructor must delete[] it instead of delete
You need to change your constructor so it's making a copy of the C string, for example:
String::String(const String& other) : str(strdup(other.str)) {}
If you use strdup above you should change your destructor appropriately, so instead of using delete you use free
String::~String() { free(str); }
It would be a good idea to change your other constructor so it's not acquiring the C string, but making a copy of it as well, this way behaviour of all constructors would be more consistent and safer, in general:
String::String(const char* aStr) : str(strdup(aStr)) {}
If you make it this way, it will work correctly whether the client code is passing you pointer allocated with malloc or new.
Replacing strdup and free with more new and delete should be easy, I'm leaving it to you as an exercise.
When you return a temporary from a function, a copy is made for the return value and the temporary is destroyed. (Sometimes the copy can be skipped via return value optimization but I don't think that's happening here).
Because your copy constructor copies the character array pointer, both objects are now pointing to the same memory. When the temporary is destroyed, it destroys the character array and now the returned object has a dangling pointer.
One of the big benefits of an immutable string class is that it can easily share buffers as you do here, without the overhead of copying. You just need a mechanism for counting the references to the buffer so it isn't deleted until the last object is deleted. You can use a std::shared_ptr with a custom deleter instead of the char * pointer.
You should also look into the Rule of Three to make sure you're implementing all the necessary functions.