I just wrote my first copy constructor and copy operator, and I'm trying to assign an object instance to an array like so:
Agent agent = Agent(navmesh, rb, m_maxPathSize);
Agent tmp = agent; // DEBUG
m_agents[idx] = agent;
The copy constructor seems to be working fine, since tmp is a perfect copy of agent (with a newly assigned m_path pointer). But when I assign agent to m_agents[idx], the latter consists of what I'd expect from the default constructor (m_path == 0, m_alive == false).
My constructors look like this:
Agent() { m_path = 0; m_alive = false; };
Agent::Agent(NavMeshNavigator* navmesh, RigidBody* rb, int maxPathSize)
: m_rb(rb), m_navmesh(navmesh), m_maxPathCount(maxPathSize)
{
m_path = new float3[maxPathSize];
};
Agent::Agent(const Agent &a)
{
memcpy(this, &a, sizeof(Agent));
if (m_path)
{
float3* oldptr = m_path;
m_path = new float3[m_maxPathCount];
memcpy(m_path, oldptr, m_maxPathCount * sizeof(float3));
}
}
Agent& Agent::operator=(const Agent &a) { return Agent(a); }
Agent::~Agent() { if (m_path) delete[] m_path; };
...
protected:
float3* m_path;
bool m_alive = true;
The constructor allocates memory for m_path using new[], the destructor frees it with delete[], the copy operator calls the copy constructor, and the copy constructor first memcopies the original before allocating a new m_path array.
In my test case, idx == 0, so that can't be it. I originally used malloc instead of new[] but got the same results. I'd say the problem is in my copy constructor/operator since I have no experience with that, but then why does it work perfectly on tmp?
EDIT:
The m_agents array is declared and destroyed like this:
NavMeshAgents(int maxAgents, int maxAgentPathSize)
: m_maxAgents(maxAgents), m_maxPathSize(maxAgentPathSize)
{
m_agents = new Agent[maxAgents];
};
~NavMeshAgents() { if (m_agents) delete[] m_agents; m_agents = 0; };
As #Evg #HolyBlackCat and #Adrian-Reinstate-Monica explained in the comments, new[] calls the default constructor for all its members. Agent tmp = agent calls the copy constructor, whereas tmp = agent would've called the assignment operator (tmp.operator=(agent)). My assignment operator was wrong, it should initialize this (and then return *this rather than return an instance.
Related
I am still somewhat new at c++, and I am a little confused about this.
Say we have struct struct_x that uses raw pointers as attributes. Should the raw pointers then be deleted in the copy assignment operator, when they are already allocated? Or should you just assign the new pointee to the pointer?
(I am aware that it is advised to use smart/unique/shared pointers).
Code example:
struct struct_x {
public:
// Some attributes.
char* m_arr nullptr;
size_t* m_len = nullptr;
size_t* m_alloc_len = nullptr;
// Default constructor.
struct_x() {
m_len = new size_t(0);
m_alloc_len = new size_t(0);
}
// Copy constructor.
// Treat "struct_x" as a unique pointer for this example.
struct_x(const struct_x& x) {
// Assign.
m_arr = new char[*x.m_alloc_len + 1];
memcpy(m_arr, x.m_arr,*x.m_len);
m_len = new size_t(*x.m_len);
m_alloc_len = new size_t(*x.m_alloc_len);
}
// Copy assignment operator.
void operator =(const struct_x& x) {
//
//
// OVER HERE
//
// Should the pointers be deleted when they are already allocated?
// Like:
if (m_arr != nullptr) { delete[] m_arr; }
if (m_len != nullptr) { delete m_len; }
if (m_alloc_len != nullptr) { delete m_alloc_len; }
// Or not?
// Assign.
...
}
}
Second question:
Do I need to delete the old m_arr after using memmove?
// Resize.
void resize(const len_t& req_len) {
using T = char; // only for this example.
if (req_len > m_alloc_len) {
if (m_alloc_len == 0) { m_alloc_len = req_len; }
m_alloc_len *= 16;
T* l_arr = new T [m_alloc_len + 1];
memmove(l_arr, m_arr, m_len);
// if (m_arr != nullptr && m_arr) { delete [] m_arr; } // Do i need to delete here, no right?
m_arr = l_arr;
}
}
If you are implementing a string-like class as an exercise, then m_len and m_alloc_len should not be pointers at all. Only m_arr should be a pointer. If you are not doing an exercise, you should be using std::string or perhaps std::vector<char>.
Having said that...
It is perfectly fine and necessary to delete owning raw pointers in assignment operators of resource-managing classes.
There is a caveat though. The assignment operator should be protected against self-assignment. If you do
my_object = my_object;
then without such protection your program will access deleted memory area. You want this:
void operator =(const struct_x& x) {
if (this != &x) {
// contents of your assignment operator
}
}
my_object = my_object is an unlikely assignment to appear in a program, but such things can and do happen, especially if there is indirection involved. Say when doing a[i] = a[j], it is perfectly reasonable to have i == j.
There are other (better) ways to protect against self-assignment. You will encounter them in due course. You probably need to learn about move semantics first.before
I am quiet new to memory management in C++. I made a BigInt class that is now fully implemented except for the destructor which is impacting performance of the program. However, when I try to implement the destructor my program crashes.
In the following code for multiplying BigInts:
BigInt& BigInt::operator*=(BigInt const& other) {
//copy of this and other
BigInt* tempThis = new BigInt(*this); //1st number
BigInt* tempOther = new BigInt(other); //2nd number
//create temps so we can use value of BigInt before it is changed
BigInt* sum = new BigInt(0); //holds the eventual answer
BigInt* i = new BigInt(0);
//add *this BigInt to sum otherTemp amount of times
//this will yield multiplication answer.
for (*i; *i < *tempOther; *i = *i + 1) {
*sum += *this;
}
*this = *sum;
return *this;
}
The destructor is called when *i = *i + 1 is called in the for loop and then I think it gets deleted in my destructor which looks like this:
// destructor
BigInt::~BigInt() {
delete[] this->bigIntVector;
}
// copy constructor
BigInt::BigInt(BigInt const& orig)
: isPositive(orig.isPositive)
, base(orig.base)
{
this->bigIntVector = new BigIntVector(*(orig.bigIntVector));
}
Once 'i' is deleted nothing works and the whole program breaks.
If someone could give me a few pointers about destructors and how to fix my problem it would be great help. Thanks.
In C++, the same (horrible) arithmetic could be implemented as follows.
BigInt& BigInt::operator*=(BigInt const& other)
{
if(other==0)
return other;
if(other> 1)
for(BigInt old=*this,i=1; i!=other; ++i)
operator+=old;
else if(other<0) {
BigInt old=*this;
*this=0;
for(BigInt i=0; i!=other; --i)
operator-=old;
}
return*this;
}
assuming that the constructor from int, the copy constructor, the increment operator++ and the addition operator+= are all properly implemented (as well as the destructor).
Unfortunately, you failed to give us more information, but your copy constructor and destructor are definitely broken:
this->bigIntVector = new BigIntVector(*(orig.bigIntVector));
is followed by
delete[] this->bigIntVector;
giving you undefined behaviour (allocating with new but deallocating with delete[] -- delete[] is for memory allocated with new[]). I suspect you meant to copy the memory from the original in the copy constructor. However, you don't. If
class BigInt {
size_t size=0; // number of some_types allocated
some_type*bigIntVector=nullptr; // ptr to memory allocated, if any
/* rest of class */
};
then the copy constructor could be implemented like (assuming size is non-static)
BigInt::BigInt(BigInt const&orig)
: size(orig.size() // copy size
, bigIntVector(size? new some_type[size] : nullptr) // allocate memory
{ std::memcpy(orig.bigIntVector, bigIntVector); } // copy memory
However, (almost) the same could be implemented much easier with
class BigInt
{
std::vector<some_type> bigIntVector;
public:
BigInt(BigInt const&) = default;
BigInt(BigInt &&) = default;
BigInt&operator=(BigInt const&) = default;
BigInt&operator=(BigInt &&) = default;
/ * rest of class */
};
when the copy and move constructors (as well as the respective assignment operators) are automatically correctly created for you. You only need to address the default constructor, for example
BigInt::BigInt() // default constructor
: bigIntVector(1,some_type(0)) {} // size=1, value=0
and the constructors from built-in integer types. If you're new to C++, avoid new and delete in favour of standard library containers.
I have a simple class:
class Histogram {
int m_width;
int m_height;
int m_sampleSize;
int m_bufferWidth;
int m_bufferHeight;
uint8* m_buffer;
int m_size;
public:
Histogram() : m_buffer(0) { }
Histogram(int width, int height, int sampleSize) {
m_buffer = new unsigned char [width*height*sampleSize];
}
~Histogram() {
my_log("destructor: buffer: %p", m_buffer);
if ( m_buffer ) { delete [] m_buffer; m_buffer = NULL; }
}
unsigned char* buffer() {
return m_buffer;
}
};
It is a member in other class:
class Other {
Histogram m_histogram;
void reset() {
my_log("reset() called: buffer: %p", m_histogram.buffer());
m_histogram = Histogram(512, 512, 2);
}
}
Now, I first create "uninitialized" object using Histogram() constructor – which sets m_buffer to NULL;
Then, I call the reset method, which does m_histogram = Histogram( 512, 512, 3 ) – the new object has m_buffer initialized via new.
So expected sequence of log messages is:
"reset() called: buffer: 0x0"
"destructor: buffer: 0x0"
But instead, I get:
"reset() called: buffer: 0x0"
"destructor: buffer: 0x072a7de"
So some irrational action is being performed. Moreover, the 0x072a7de address is displayied when I also delete the second object (created with "larger" constructor, with three int parameters).
You MUST realize copy-ctor and assignment operator for your class Histogram, since
m_histogram = Histogram(512, 512, 2);
is assignment operator call. Implicit operator = bitwise copy members of your class.
And you must use delete[] in destructor, not delete, since you allocate an array.
First of all, since you are pointing to a dynamically allocated array, you need to use operator delete[]
delete[] m_buffer;
Second, and more importantly, since you have dynamically allocated memory, you should follow the rule of three and implement a copy constructor, and assignment operator, as well as fixing the destructor.
What happens now is that your (compiler synthesized) assignment operator is making a "shallow" copy, i.e. it is copying the pointer. Then you will hve multiple destructors trying to delete it. You are invoking undefined behaviour.
You could really save yourself a lot of trouble by using an std::vector<uint8> as a buffer.
I have a header file:
using namespace std;
class IntList{
private:
int *Intl;
int Capacity;
int Count;
public:
IntList(int capacity){
Capacity = capacity;
Count = 0;
Intl = new int[capacity];
}
~IntList(){
delete Intl;
}
//adds the integers of the specified collection to the end of the List; return false if the new Count will be greater than Capacity
bool AddRange(const IntList &items){
//int *Temp = items.;
if(items.Count > Capacity - Count){
return false;
}else{
for(int i = 0; i <items.Count; i++){
Intl[Count] = items.Intl[i];
Count++;
}
return true;
}
}
};
But I don't know why I can't return value to IntList object in there:
//creates a copy of a range of elements in the source List
IntList GetRange(int index, int count){
IntList A(count);
for(int i = 0; i < count; i++){
A.Intl[i] = Intl[index -1 +i];
}
return A;
}
I want to return value of A whose type is IntList but I meet an error on "_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) in visual studio 2010. How can I repair it?
Because int *Intl; is an object you manually manage, you'll need to implement the copy constructor for your class.
The function GetRange returns by value. The local object A gets destroyed, and its member Intl gets deleted in the destructor, so your copy (as copied by the default copy constructor) is only a shallow one, and will contain an invalid member.
EDIT: As Rob correctly pointed out, you'll also need to implement the assignment operator (you already have a destructor).
For an object which is returned by value makes a call to copy-constructor . You must make a copy-constructor and define it so that you get appropriate results. Returning by reference actually does not require call to copy constructor but should not be made for a temporary object. Also since you have a pointer type as member variable in class . It would be appropiate for you to overload the = operator. It should be define properly to avoid memory leak. Do something like this Intlist a=GetRange(index,count) . Also you should create a copy constructor for this . Your code also has a bug that it doesnot overload= operator for class Intlist .
you can write a copy constructor something like this :-
Intlist::Intlist(const Intlist& cSource)
{
capacity = cSource.capacity;
count= cSource.count;
// Intl is a pointer, so we need to deep copy it if it is non-null
if (cSource.Intl)
{
// allocate memory for our copy
Intl = new int[capacity];
// Copy the Intl into our newly allocated memory in for loop
for(i=0;i<capacity;i++)
{
// copy part
}
}
else
intl = NULL;
}
Just an e.g how you should write it.
I have these variables:
char** wordList_;
int wordListCapacity_;
int* wordCountList_;
char* fileName_;
int nUniqueWords_;
int nTotalWords_;
int nTotalCharacters_;
My copy constructor:
FileIndex::FileIndex(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
cout << "Jiasd?" << endl;
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nTotalWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
}
My overloaded assignment operator:
FileIndex& FileIndex::operator=(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nUniqueWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for (int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = new char[strlen(fi.wordList_[i])+1];
strcpy(wordList_[i], fi.wordList_[i]);
wordCountList_[i] = fi.wordCountList_[i];
}
return *this;
}
Whenever I create a FileIndex (called FirstIndex) and initialize the member variables with something meaningful (not NULL) I have these lines to test the copy constructor and assignment operator:
FileIndex secondIndex = firstIndex;
FileIndex thirdIndex;
secondIndex = thirdIndex; // Segmentation fault here
I get a segmentation fault with the assignment operator but I have a feeling it may be because of faulty code in the copy constructor. That being said, if there's an error in the copy constructor then there's also probably one in the assignment operator.
Thanks in advance for the help!
I think you want to use std::string and std::vector<T> for your class. Also, for the purpose of what goes wrong it would be necessary to see the default constructor and the destructor. From the look of you setup it seems that you may e.g. not have initialized some of the members in your default constructor. Also, you assignment operator has several resource leaks and will be pretty bad off if you try self assignment. In general, I'd recommend to implement assignment operators like this:
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
This leverages the work done for the copy constructor and use a swap() member which is generally very easy to do.
Check out your copy constructor.
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
The problem is with wordList_[i] = fi.wordList_[i];. You are not allocating new memory and doing a strcpy here as you do in the assignment operator. Instead your new copy is actually pointing to data from the instance that it is copying from. I believe this may be what David Schwartz was alluding to.
It looks as if you may not initialize wordListCapacity_ correctly (it's hard to tell since you don't show the default ctor). Since it is an int, it can have a negative value, which can cause a segfault when you attempt wordList_ = new char*[wordListCapacity_];. There may be other problems.