C++ Dynamic Member Arrays Deleted Right Before Destructor Called - c++

I'm working on an AI project and have started to implement a NeuralNetwork class. I just want to get something basic down so I used some malloc statements with some placement news and finally delete[]s.
However, once the NeuralNetwork object (created on the stack in the main function) is about to be deleted (I set a breakpoint at the start of the destructor), my arrays seem to have been prematurely deleted (value 0xcccccccc) and the delete[] statements therefore throw access violations.
Through further investigation I found out that this deleting happens right between the last Vector object being destructed and the start of the destructor of my NeuralNetwork object being called.
I made sure to implement both copy constructors and assignment operators to make sure no copying was taking place without me noticing.
I'm really baffled with this problem and hope that someone can catch my mistake. Source code will follow:
NeuralNetwork::NeuralNetwork(const std::initializer_list<size_t>& l):
m_size(l.size() - 1),
m_weights(static_cast<Matrix<double>*>(malloc(sizeof(Matrix<double>) * m_size))),
m_biases(static_cast<Vector<double>*>(malloc(sizeof(Vector<double>) * m_size)))
{
size_t index = 0;
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr, ++index)
{
new (m_weights + index) Matrix<double>(*next, *itr);
new (m_biases + index) Vector<double>(*next);
}
}
NeuralNetwork::NeuralNetwork(const NeuralNetwork& nn) :
m_size(nn.m_size),
m_weights(static_cast<Matrix<double>*>(malloc(sizeof(Matrix<double>)* m_size))),
m_biases(static_cast<Vector<double>*>(malloc(sizeof(Vector<double>)* m_size)))
{
for (size_t index = 0; index < m_size; ++index)
{
new (m_weights + index) Matrix<double>(nn.m_weights[index]);
new (m_biases + index) Vector<double>(nn.m_biases[index]);
}
}
NeuralNetwork::NeuralNetwork(NeuralNetwork&& nn) noexcept :
m_size(nn.m_size),
m_weights(nn.m_weights),
m_biases(nn.m_biases)
{
nn.m_size = 0;
nn.m_weights = nullptr;
nn.m_biases = nullptr;
}
NeuralNetwork::~NeuralNetwork()
{
delete[] m_weights; // exception thrown here, value is 0xcccccccc, nullptr
delete[] m_biases;
}
Main code:
int main()
{
NeuralNetwork nn{ 2, 1 };
Vector<double> input(2);
input.Get(0) = 0.5;
input.Get(1) = 0.25;
Vector<double> output = nn.Forward(input); // just does the math, nothing special
for (size_t i = 0; i < output.GetSize(); ++i)
std::cout << output.Get(i) << " ";
std::cout << std::endl;
}
In case any important source code is missing please let me know, thanks!

There is a big difference between malloc/free and new/delete and new[] / delete[]
malloc will allocate an unformated chunk of memory and free will free it
new will allocate and initialize that region and delete will call the destructor
sometimes it might work to use malloc and delete but it's a bad idea
new[] will also keep a few extra info before the the returned memory to know how many objects need to be deleted
Usefull links:
https://www.geeksforgeeks.org/placement-new-operator-cpp/

You should never write such code:
You should never have to use malloc/free in a C++ program.
Allocation and desallocation should match.
Dynamic memory allocation has surely more overhead that default initialization you try to avoid.
Your code would miserably failed if initializer list is empty.
Code has memory leaks.
If you define a copy constructor, then you should also define assignment operator (same for move constructor).
Standard library already do most relavant optimization. For example,, for a std::vector the constructor of an item will be only called when you call emplace_back.
You should really write standard code. It does not worth the trouble to write bugged code for marginal performance improvement.
Your class declaration should really look something like:
class NeuralNetwork
{
public:
NeuralNetwork(const std::initializer_list<size_t>& l);
// Other constructors as appropriate here…
private:
std::vector<Matrix<double>> m_weights;
std::vector<Vector<double>> m_biases;
};
And constructor should look similar to that (code not tested):
NeuralNetwork::NeuralNetwork(const std::initializer_list<size_t>& l):
{
if (l.empty()
{
// might assert in debug or throw an exception...
return;
}
m_weights.reserve(m_size);
m_biases.reserve(m_size);
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr, ++index)
{
m_weights.emplace(*next, *itr);
m_biases.emplace(*next);
}
}
Other constructors, assignment operators and destructors should be easier to implement, more robust and performance very similar.

As you are using C++11 features already, you can also use std::vector::emplace_back(), which will deal with placement new internally.
Example:
#include <iostream>
#include <initializer_list>
#include <vector>
template<class T> class Matrix {
public:
Matrix() {std::cout << "Matrix() " << this << std::endl;}
Matrix(int width,int height):w(width),h(height) {std::cout << "Matrix(" << w << "x" << h << ") " << this << std::endl;}
~Matrix() {std::cout << "Matrix(" << w << "x" << h << ") " << this << " goodbye" << std::endl;}
private:
int w,h;
};
class NN {
public:
NN()=default;
NN(const std::initializer_list<size_t> &l);
private:
std::vector<Matrix<double>> m_weights;
};
NN::NN(const std::initializer_list<size_t> &l) {
m_weights.reserve(l.size()-1); // or deal with move constructors
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr)
{
m_weights.emplace_back(*next, *itr);
}
}
int main() {
NN test{2,3,3,2};
return 0;
}
Output (from https://ideone.com/yHGAMc):
Matrix(3x2) 0x5638f59aae70
Matrix(3x3) 0x5638f59aae78
Matrix(2x3) 0x5638f59aae80
Matrix(3x2) 0x5638f59aae70 goodbye
Matrix(3x3) 0x5638f59aae78 goodbye
Matrix(2x3) 0x5638f59aae80 goodbye
So the default constructor was not involved and objects were destructed properly.

Related

C++ Free pointers stored in a vector

So, I have these functions (Game inherits from GameInterface; they both currently have no purpose, I'm just testing whether what I have in mind can be done or not.):
vector<GameInterface*> Game::generateChildren() {
vector<GameInterface*> vgf;
// make 10 copies of this object, increasing the 'npawns' attribute
Game* gi = this;
for (int i = 0; i < 10; ++i) {
gi = new Game(*gi);
vgf.push_back(gi);
++gi->npawns;
}
return vgf;
}
and
const int Library::getBestMovement(GameInterface* gf) const {
vector<GameInterface*> vgf = gf->generateChildren();
int nchildren = vgf.size();
// outputs "child #i has (i+1) pawns" for i in 0..9
for (int i = 0; i < nchildren; ++i) {
cout << "child #" << i << " has " << vgf[i]->num_of_pawns() << " pawns" << endl;
}
// missing some dynamic memory management (deleting the pointers in vgf)
return 0;
}
Since the "children" objects are created with new, and they will no longer be used once getBestMovement ends, I assume I have to free the pointers inside vgf.
The problem is that I've tried everything from
for (auto it = vgf.begin(); it != vgf.end(); ++it)
delete *it;
to using smart pointers, but I always get the same debug assertion error when I execute the program: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse).
Any idea about what the problem is? Thanks
EDIT:
Ok, I had class Game : virtual public GameInterface. Removed the virtual keyword and it works fine now. I don't know why, though (I didn't even know what he virtual keyword did; I'm mostly testing stuff since I'm kind of new to the language, so please bear with me)
EDIT 2:
Forcing the GameInterface destructor to be virtual seems to be the proper solution.
The following code should be vaguely correct:
#include <memory>
#include <vector>
struct GameInterface
{
virtual ~GameInterface() {}
virtual std::vector<std::unique_ptr<GameInterface>> generateChildren() = 0;
};
struct Game : GameInterface
{
std::vector<std::unique_ptr<GameInterface>> generateChildren() override
{
std::vector<std::unique_ptr<GameInterface>> result;
for (int i = 0; i != 10; ++i)
{
result.emplace_back(new Game(result.empty()
? *this
: *result.back()));
++result.back()->npawns;
}
return result;
}
};
int getBestMovement(GameInterface* gf)
{
auto v = gf->generateChildren();
// ...
return 0;
}

Even the Copy Assignment Operator can't assist

Kindly help me figure out where the issue is. I have followed the rule of three as well and made several modifications to the code.
#include <iostream>
using namespace std;
class AStack {
public:
AStack();
AStack(int);
AStack(const AStack&);
~AStack();
AStack& operator = (const AStack& s);
void push(int);
int pop();
int top();
bool isEmpty();
void flush();
private:
int capacity ;
int* a;
int index = -1; // Index of the top most element
};
AStack::AStack() {
a = new int[25];
capacity = 25;
}
AStack::AStack(int size) {
a = new int[size];
capacity = size;
}
AStack::AStack(const AStack& s) {
capacity = s.capacity;
delete[] a; // To avoid memory leak
a = new int[capacity];
for (int i = 0; i < capacity; i++) {
a[i] = s.a[i];
}
index = s.index;
}
AStack::~AStack() {
delete[] a;
}
AStack& AStack::operator = (const AStack& s) {
capacity = s.capacity;
delete[] a; // To avoid memory leak
int* a = new int[capacity];
for (int i = 0; i < capacity; i++) {
a[i] = s.a[i];
}
index = s.index;
return *this;
}
void AStack::push(int x) {
if (index == capacity - 1) {
cout << "\n\nThe stack is full. Couldn't insert " << x << "\n\n";
return;
}
a[++index] = x;
}
int AStack::pop() {
if (index == -1) {
cout << "\n\nNo elements to pop\n\n";
return -1;
}
return a[index--];
}
int AStack::top() {
if (index == -1) {
cout << "\n\nNo elements in the Stack\n\n";
return -1;
}
return a[index];
}
bool AStack::isEmpty() {
return (index == -1);
}
void AStack::flush() {
if (index == -1) {
cout << "\n\nNo elements in the Stack to flush\n\n";
return;
}
cout << "\n\nFlushing the Stack: ";
while (index != -1) {
cout << a[index--] << " ";
}
cout << endl << endl;
}
AStack& reverseStack(AStack& s1) {
AStack s2;
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
s1 = s2;
return s1;
}
int main() {
AStack s1;
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
s1.push(5);
s1 = reverseStack(s1);
cout << "\n\nFlushing s1:\n";
s1.flush();
system("pause");
return 0;
}
I fail to understand how even after defining the appropriate copy assignment operator, the values in s1 after returning from the function are garbage values.
If your copy constructor is correct, and your destructor is correct, your assignment operator could be written in a much easier and safer fashion.
Currently, your assignment operator has two major flaws:
No check for self-assignment.
Changing the state of this before you know you can successfully allocate the
memory (your code is not exception safe).
The reason for your error is that the call to reverseStack returns a reference to the current object. This invoked the assignment operator, thus your assignment operator was assigning the current object to the current object. Thus issue 1 above gets triggered.
You delete yourself, and then you reallocate yourself, but where did you get the values from in the loop to assign? They were deleted, thus they're garbage.
For item 2 above, these lines change this before you allocate memory:
capacity = s.capacity;
delete[] a; // To avoid memory leak
What happens if the call to new[] throws an exception? You've messed up your object by not only changing the capacity value, but you've also destroyed your data in the object by calling delete[] prematurely.
The other issue (which needs to be fixed to use the copy/swap idiom later in the answer), is that your copy constructor is deallocating memory it never allocated:
AStack::AStack(const AStack& s) {
capacity = s.capacity;
delete[] a; // ?? What
Remove the line with the delete[] a, since you are more than likely calling delete[] on a pointer that's pointing to garbage.
Now, to rid you of these issues with the assignment operator, the copy/swap idiom should be used. This requires a working copy constructor and a working destructor before you can utilize this method. That's why we needed to fix your copy constructor first before proceeding.
#include <algorithm>
//...
AStack& AStack::operator = (AStack s)
{
std::swap(capacity, s.capacity);
std::swap(a, s.a);
std::swap(index, s.index);
return *this;
}
Note that we do not need to check for self assignment, as the object that is passed by value is a brand new, temporary object that we are taking the values from, and never the current object's values (again, this is the reason for your original code to fail).
Also, if new[] threw an exception, it would have been thrown on the call to the assignment operator when creating the temporary object that is passed by value. Thus we never get the chance to inadvertently mess up our object because of new[] throwing an exception.
Please read up on what the copy/swap idiom is, and why this is the easiest, safest, and robust way to write an assignment operator. This answer explains in detail what you need to know:
What is the copy-and-swap idiom?
Here is a live example of the fixed code. Note that there are other changes, such as removing the default constructor and making the Attack(int) destructor take a default parameter of 25.
Live Example: http://ideone.com/KbA20D

2D Array of Pointers -> Classes

I am having problems with my constructor in class World.
I created a 2D array with pointers where each entry in the array is of type Organism, hence the line of code:
Organism* grid[20][20];
When I run my program, I only see
hello
and after that, I get a message saying that my program has stopped working. I'm pretty sure it's the line of code
grid[i][j]->symbol = ' ';
that's causing the problem. Just to see what would happen, I changed that line to
grid[i][j];
and didn't get any errors. But, the moment I put ->, I seem to get errors.
Is there a reason why my program stops working after I put ->? Any help would be appreciated.
This is my code:
#include <iostream>
using namespace std;
class Organism
{
public:
char symbol;
};
class World
{
public:
World();
private:
Organism* grid[20][20];
};
int main()
{
World world;
return 0;
}
World::World()
{
for(int i = 0; i < 20; i++)
for(int j = 0; j < 20; j++)
{
cout << "hello" << endl;
grid[i][j]->symbol = ' ';
cout << "here" << endl;
}
}
You have an array of pointers to Organism.
Pointers can not hold any data other than an address. They can only point to memory (that holds data).
Your array's pointers does not point to anything, thats why you get undefined behaviour when you try to assign data to where the pointers point at.
You need to allocate memory for your array.
Organism grid[20][20]; // Create an array of objects (not pointers).
/* ... */
grid[i][j].symbol = ' ';
Same using dynamic memory:
class World {
public:
World();
~World(); // Rule of Three.
World(const World&) = delete; // Rule of Three.
World& operator=(const World&) = delete; // Rule of Three.
private:
Organism** grid;
};
World::World() {
grid = new Organism*[20]; // Allocate memory to point to.
for(std::size_t i = 0; i != 20; ++i) {
grid[i] = new Organism[20]; // Allocate memory to point to.
for(std::size_t j = 0; j != 20; ++j) {
cout << "hello" << endl;
grid[i][j].symbol = ' ';
cout << "here" << endl;
}
}
}
// Destructor needed to deallocate memory (otherwise it will leak).
World::~World()
{
for (std::size_t i = 0; i != 20; ++i) {
delete[] grid[i];
}
delete[] grid;
}
Now you can see how complicated it gets when using dynamic memory and why it's recommended to prefer to use automatic storage duration (i.e. create objects, not pointers and new/delete).
Even better is to use a container from the standard library for storing your elements as e.g. std::vector.
Related:
What is The Rule of Three?
Change
Organism* grid[20][20];
To
Organism grid[20][20];
and use
grid[i][j].symbol = '';
instead of
grid[i][j]->symbol = '';
and add a default constructor to Organism
class Organism
{
Organism();
...
}
or make Organism a struct
struct Oranism
{
...
}

What is wrong with my park_car function?

I'm again doing a task for school and I'm implementing it slowly, I don't know why my park_car function is not working, I just wanted to make a test and the program crashes ... here is my code.
PS: I can't change the ***p2parkboxes because it is given in the starter file like most other variables. I just want to see the first element of Floor 0 as : HH-AB 1234. Your help is most appreciated.
PS2: I can't use the std::string as well it isn't allowed for the task.
#include <iostream>
#include <cstring>
using namespace std;
#define EMPTY "----------"
class Parkbox{
char *license_plate; // car's license plate
public:
Parkbox(char *s = EMPTY); // CTOR
~Parkbox(); // DTOR
char *get_plate(){return license_plate;}
};
class ParkingGarage{
Parkbox ***p2parkboxes;
//int dimensions_of_parkhouse[3]; // better with rows,columns,floors
int rows,columns,floors; // dimensions of park house
int total_num_of_cars_currently_parked;
int next_free_parking_position[3];
// PRIVATE MEMBER FUNCTION
void find_next_free_parking_position();
public:
ParkingGarage(int row, int col, int flr);// CTOR,[rows][columns][floors]
~ParkingGarage(); // DTOR
bool park_car(char*); // park car with license plate
bool fetch_car(char*); // fetch car with license plate
void show(); // show content of garage floor
// by floor
};
Parkbox::Parkbox(char *s ) { // CTOR
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
//cout << "ParkBox CTOR" << endl;
}
Parkbox::~Parkbox() { // DTOR
delete [] license_plate;
//cout << "ParkBox DTOR" << endl;
}
ParkingGarage::ParkingGarage(int row, int col, int flr){
rows = row; columns = col; floors = flr;
p2parkboxes = new Parkbox**[row];
for (int i = 0; i < row; ++i) {
p2parkboxes[i] = new Parkbox*[col];
for (int j = 0; j < col; ++j)
p2parkboxes[i][j] = new Parkbox[flr];
}
}
ParkingGarage::~ParkingGarage(){
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j)
delete [] p2parkboxes[i][j];
delete [] p2parkboxes[i];
}
delete [] p2parkboxes;
}
void ParkingGarage::show(){
int i,j,k;
for (i = 0 ; i < floors; i++){
cout << "Floor" << i << endl;
for (j=0;j<rows;j++){
for (k=0;k<columns;k++){
cout << p2parkboxes[j][k][i].get_plate() << " ";
}
cout << endl;
}
}
}
bool ParkingGarage::park_car(char*s){
p2parkboxes[0][0][0] = Parkbox(s); //test
//p2parkboxes[0][0][0] = s; //test
return true;
}
int main(void) {
// a parking garage with 2 rows, 3 columns and 4 floors
ParkingGarage pg1(2, 3, 4);
pg1.park_car("HH-AB 1234");
/*pg1.park_car("HH-CD 5678");
pg1.park_car("HH-EF 1010");
pg1.park_car("HH-GH 1235");
pg1.park_car("HH-IJ 5676");
pg1.park_car("HH-LM 1017");
pg1.park_car("HH-MN 1111"); */
pg1.show();
/*pg1.fetch_car("HH-CD 5678");
pg1.show();
pg1.fetch_car("HH-IJ 5676");
pg1.show();
pg1.park_car("HH-SK 1087");
pg1.show();
pg1.park_car("SE-AB 1000");
pg1.show();
pg1.park_car("PI-XY 9999");
pg1.show(); */
return 0;
}
You did not declare the copy constructor for the Parkbox class. So, the line
p2parboxes[0][0][0] = Parkbox(s)
creates something (instance of Parkbox with a char* pointer) on the stack (and deletes it almost immediately). To correct this you might define the
Parkbox& operator = Parkbox(const Parkbox& other)
{
license_plate = new char[strlen(other.get_plate())+1];
strcpy(license_plate, other.get_plate());
return *this;
}
Let's see the workflow for the
p2parboxes[0][0][0] = Parkbox(s)
line.
First, the constructor is called and an instance of Parkbox is created on stack (we will call this tmp_Parkbox).
Inside this constructor the license_plate is allocated and let's say it points to 0xDEADBEEF location.
The copying happens (this is obvious because this is the thing that is written in code) and the p2parboxes[0][0][0] now contains the exact copy of tmp_Parkbox.
The scope for tmp_Parkbox now ends and the destructor for tmp_Parkbox is called, where the tmp_Parkbox.license_plate (0xDEADBEEF ptr) is deallocated.
p2parboxes[0][0][0] still contains a "valid" instance of Parkbox and the p2parboxes[0][0][0].license_plate is still 0xDEADBEEF which leads to the undefined behaviour, if any allocation occurs before you call the
cout << p2parboxes[0][0][0].license_plate;
Bottom line: there is nothing wrong with the line itself, the problem is hidden within the implementation details of the '=' operator.
At this point it is really better for you to use the std::string for strings and not the razor-sharp, tricky and explicit C-style direct memory management mixed with the implicit C++ copy/construction semantics. The code would also be better if you use the std::vector for dynamic arrays.
The problem here is that you do not have deep copy assignment semantics. When you assign a temporary Parkbox to the Parkbox in the parking garage, the compiler generated assignment operator makes a shallow copy of the pointer license_plate, leaving both Parkboxes pointing at the same memory location. Then the temporary Parkbox goes out of scope and deletes license_plate. Since the other Parkbox is pointing at the same spot its license_plate gets deleted, too.
There are a couple solutions. One way to solve the problem is to define an assignment operator and a copy constructor that provide proper semantics, i.e. that perform deep copies of the license plate string. The better option, and the one that makes better use of C++, is to use std::strings instead of manually allocated C-strings. I strongly suggest the second approach, though working through the first might be instructive.
From the OP:
I solved the Problem with :
void Parkbox::change_plate(char *s){
delete [] license_plate;
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
}

STL container leak

I'm using a vector container to hold instances of an object which contain 3 ints and 2 std::strings, this is created on the stack and populated from a function in another class but running the app through deleaker shows that the std::strings from the object are all leaked. Here's the code:
// Populator function:
void PopulatorClass::populate(std::vector<MyClass>& list) {
// m_MainList contains a list of pointers to the master objects
for( std::vector<MyClass*>::iterator it = m_MainList.begin(); it != m_MainList.end(); it++ ) {
list.push_back(**it);
}
}
// Class definition
class MyClass {
private:
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Code causing the problem:
std::vector<MyClass> list;
PopulatorClass.populate(list);
When this is run through deleaker the leaked memory is in the allocator for the std::string classes.
I'm using Visual Studio 2010 (CRT).
Is there anything special I need to do to make the strings delete properly when unwinding the stack and deleting the vector?
Thanks,
J
May be Memory leak with std::vector<std::string> or something like this.
Every time you got a problem with the STL implementation doing something strange or wrong like a memory leak, try this :
Reproduce the most basic example of what you try to achieve. If it runs without a leak, then the problem is in the way you fill the data. It's the most probable source of problem (I mean your own code).
Not tested simple on-the-fly example for your specific problem :
#include <string>
#include <sstream>
// Class definition
struct MyClass { // struct for convenience
std::string m_Name;
std::string m_Description;
int m_nType;
int m_nCategory;
int m_nSubCategory;
};
// Prototype of populator function:
void populate(std::vector<MyClass>& list)
{
const int MAX_TYPE_IDX = 4;
const int MAX_CATEGORY_IDX = 8;
const int MAX_SUB_CATEGORY_IDX = 6;
for( int type_idx = 0; type_idx < MAX_TYPE_IDX ; ++type_idx)
for( int category_idx = 0; category_idx < MAX_CATEGORY_IDX ; ++category_idx)
for( int sub_category_idx = 0; sub_category_idx < MAX_SUB_CATEGORY_IDX ; ++sub_category_idx)
{
std::stringstream name_stream;
name_stream << "object_" << type_idx << "_" << category_idx << "_" << sub_category_idx ;
std::stringstream desc_stream;
desc_stream << "This is an object of the type N°" << type_idx << ".\n";
desc_stream << "It is of category N°" << category_idx << ",\n";
desc_stream << "and of sub-category N°" << category_idx << "!\n";
MyClass object;
object.m_Name = name_stream.str();
object.m_Description = desc_stream.str();
object.m_nType = type_idx;
m_nCategory =
m_nSubCategory =
list.push_back( object );
}
}
int main()
{
// Code causing the problem:
std::vector<MyClass> list;
populate(list);
// memory leak check?
return 0;
}
If you still got the memory leak, first check that it's not a false-positive from your leak detection software.
Then if it's not, google for memory leak problems with your STL implementation (most of the time on the compiler developer website). The implementor might provide a bug tracking tool where you could search in for the same problem and potential solution.
If you still can't find the source of the leak, maybe try to build your project with a different compiler (if you can) and see if it have the same effect. Again if the leak still occurs, the problem have a lot of chances to come from your code.
Probably same root issue as Alexey's link. The shipped version has broken move code for basic_string. MS abandoned us VC10 users, so you must fix it yourself. in xstring file you have this:
_Myt& assign(_Myt&& _Right)
{ // assign by moving _Right
if (this == &_Right)
;
else if (get_allocator() != _Right.get_allocator()
&& this->_BUF_SIZE <= _Right._Myres)
*this = _Right;
else
{ // not same, clear this and steal from _Right
_Tidy(true);
if (_Right._Myres < this->_BUF_SIZE)
_Traits::move(this->_Bx._Buf, _Right._Bx._Buf,
_Right._Mysize + 1);
else
{ // copy pointer
this->_Bx._Ptr = _Right._Bx._Ptr;
_Right._Bx._Ptr = 0;
}
this->_Mysize = _Right._Mysize;
this->_Myres = _Right._Myres;
_Right._Mysize = 0;
_Right._Myres = 0;
}
return (*this);
}
Note the last
_Right._Myres = 0;
that should happen only under the last condition, for the short case _Right should better be left alone.
As the capacity is set to 0 instead of 15, other code will take unintended branch in function Grow() when you assign another small string and will allocate a block of memory just to trample over the pointer with the immediate string content.