Unusual memory leak in queue - code hangs on exit - c++

i need to create queue of classes to be processed by a thread afterwards. The problem is that every time i add a reference to an object, queue assigns memory for that class causing huge memory leaks. This also leads to program hang on exit.
Adding 12345678 references to an object of TaskClass to TaskQueue causes 137MB memory leak.
Notice that memory is not freed when calling queue.pop().
class TaskQueue:
template <class tjob>
class TaskQueue
{
private:
std::queue<tjob> _taskqueue;
public:
TaskQueue()
{
//constructor goes here
}
//add task to queue
template < typename Class>
bool AddTask( Class &PClass)
{
_taskqueue.push(PClass);
return true;
}
bool ProcessQueue()
{
while (!_taskqueue.empty())
{
_taskqueue.front().run();
_taskqueue.pop();
}
return true;
}
//run a function pointer
template < typename Task >
bool RunTask( Task task){
task();
return true;
}
//call class entry point member .run
template < typename Class>
bool RunClass ( Class& PClass){
PClass.run();
return true;
}
//return remaining tasks
int GetRemainingTasks(){
return _taskqueue.size();
}
};
class TaskClass:
class TaskClass
{
protected:
int *ptr_x;
public:
TaskClass() {
std::cout << "TaskClass Constructor called\n";
ptr_x = new int(0);
}
bool run(){
*ptr_x = *ptr_x + 1;
return true;
}
bool printx(){
std::cout << "x is now " << *ptr_x << std::endl;
return true;
}
~TaskClass(){
//std::cout << "TaskClass destructor called!\n";
}
};
main:
int main()
{
TaskClass job1;
int nojobs = 12345678;
TaskQueue<TaskClass> TestQueue;
std::cout << "Preparing Queue... Adding " << nojobs << " tasks.. "; //std::cin.get();
for (int i=0;i<nojobs;i++)
TestQueue.AddTask(job1);
std::cout << "Done!\n"; //std::cin.get();
std::cout << "Processing Queue... ";
TestQueue.ProcessQueue();
std::cout << "Done!\n";
job1.printx();
std::cout << "Remaining tasks: " << TestQueue.GetRemainingTasks() << std::endl;
//std::cin.get();
//exit(0);
return 0;
}

The memory leak is
ptr_x = new int(0);
because you never delete that memory. At a minimum, you need to delete it in the destructor, and also add a copy constructor which deep-copies it.
A better solution is to replace the pointer with a simple
class TaskClass
{
protected:
int x;
(although I don't see why it would be static as in billz's answer).
Incidental infelicities:
unnecessary templating:
template < typename Class>
bool AddTask( Class &PClass) {
the only valid type for Class is the class template argument tjob, so why template this method at all? And why require a non-const ref to something you can only copy?
bool AddTask(tjob const &job) {
_taskqueue.push(job);
return true;
}
is better. Similarly for the other templated methods.
i need to create queue of classes
No, you don't, and in fact you can't (I suppose you could create a queue of typeinfo_t if you really wanted). You need to create a queue of objects. Fortunately that's what you're actually doing, since calling an object reference PClass doesn't make it a pointer-to-class.
This might seem (and indeed be) pedantic, but it's generally easier for everyone if you get the terminology right.

Related

C++ pool allocator program crashing only on console close

I am looking at this pool allocator implementation. I have actually modified it a bit and my full code is:
template <class T, size_t T_per_page = 200>
class PoolAllocator
{
private:
const size_t pool_size = T_per_page * sizeof(T);
std::vector<T *> pools;
size_t count;
size_t next_pos;
void alloc_pool() {
next_pos = 0;
void *temp = operator new(pool_size);
pools.push_back(static_cast<T *>(temp));
}
public:
PoolAllocator() {
count = 0;
alloc_pool();
}
void* allocate() {
if (next_pos == T_per_page)
alloc_pool();
void* ret = pools.back() + next_pos;
++next_pos;
++count;
return ret;
}
size_t getSize() const
{
return T_per_page * (pools.size() - 1) + next_pos;
}
size_t getCount() const
{
return count;
}
size_t getCapacity() const
{
return T_per_page * pools.size();
}
T* get(size_t index) const
{
if (index >= getCount()) { return NULL; }
size_t poolIndex = index / T_per_page;
return pools[poolIndex] + (index % T_per_page);
}
~PoolAllocator() {
std::cout << "POOL ALLOCATOR DESTRUCTOR CALLED" << std::endl;
while (!pools.empty()) {
T *p = pools.back();
size_t start = T_per_page;
if (pools.size() == 1){
start = next_pos;
}
std::cout << "start: " << start << std::endl;
for (size_t pos = start; pos > 0; --pos)
{
std::cout << "pos: " << pos << std::endl;
p[pos - 1].~T();
}
operator delete(static_cast<void *>(p));
pools.pop_back();
}
}
};
template<class T>
PoolAllocator<T>& getAllocator()
{
static PoolAllocator<T> allocator;
return allocator;
}
class Node
{
private:
int id;
std::vector<float> vertices;
public:
Node() : id(42)
{
std::cout << "Node constructor called" << std::endl;
}
~Node(){ std::cout << "Node destructor called" << std::endl; }
void* operator new(size_t size)
{
std::cout << "Node operator new called" << std::endl;
return getAllocator<Node>().allocate();
}
void operator delete(void*)
{
std::cout << "Node operator delete called" << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Node* n1 = new Node();
Node* n2 = new Node();
Node* n3 = new Node();
Node* n4 = new Node();
std::cout << "Count: " << getAllocator<Node>().getCount() << " size: " << getAllocator<Node>().getSize() << " capacity: " << getAllocator<Node>().getCapacity() << std::endl;
while (true){}
return 0;
}
When I run this code in visual studio it appears to work correctly up until I close the console at which point I get an access violation error. I have tried manually calling the destructor on the allocator and it appears to work properly but I must be making a mistake somewhere. The error I get is:
Can anyone spot where I am making my mistake?
Edit 1:
Upon further investigation it will still crash even without the new Node lines in main. Seems to be related to getAllocator() method and how the destructor is called maybe? Or the fact that the allocator is static??
Edit 2:
I actually don't think it has anything to do with my allocator at all! If I try the code:
class Node2
{
private:
int x;
public:
Node2():x(42){std::cout << "Node2 constructor called" << std::endl;};
Node2(const Node2& other){ std::cout << "Node2 copy constructor called" << std::endl; };
~Node2(){ std::cout << "Node2 destructor called" << std::endl; };
};
Node2& Test(){
static Node2 myIndex;
return myIndex;
}
int _tmain(int argc, _TCHAR* argv[])
{
Test();
while (true){}
return 0;
}
It results in the same error! The plot thickens. I assumed being new to writing custom allocators that the allocator code was the issue. Still not sure why exactly this error is happening for my smaller code yet...
Writing an answer as I can't comment on the question.
I can't spot any obvious error in the last code. Are you sure you are compiling the right file and not an old unsaved version and such?
You can try to remove the line
while (true){}
And let the program just end normally.
Also, you can try to run your code in debug mode, single-stepping the instructions to find the one causing troubles.
I can spot some problems with that pool allocator.
PoolAllocator owns resources, but there's neither special copy constructor nor assignment. Most likely you should declare them deleted. And provide move-constructor and move-assignment. Though not a factor in this particular example, it may protect you from absendmindedly returning an allocator by value.
Function alloc_pool() resets next_pos before the new chunk is allocated. An exception, if ever thrown by operator new, would leave the pool in an inconsistent state.
Likewise, an exception in pools.push_back() would see the new chunk leaked. I believe std::vector<std::vector<std::byte>> would do just right, what with modern vectors being moveable. But if you absolutely want to use a vector of raw pointers, you should reserve extra space in pools, then allocate new chunk, and only then call push_back and modify the state.
Constructor of PoolAllocator may throw for no good reason.
Since allocate() method have to call alloc_pool() anyway, why calling it in the constructor? You could have a trivial noexcept constructor simply by leaving all allocation work to allocate().
PoolAllocator::allocate() and PoolAllocator::~PoolAllocator() are not symmetric. The former returns raw memory with no initialized object, while the latter assumes there's a properly constructed object in every allocated slot. That assumption is dangerous and very fragile. Imagine, for example, that T::T() throws.
It seems that getSize() and getCount() always return the same value. Is it intended?
Destructor will delete next_pos objects in the first pool, pools[0], and T_per_page objects in every other pool. But it should delete next_pos objects in the last pool.
You're in for wonderful bugs if T:~T() called from destructor of the pool ever tried to allocate another object from that very same pool. Such scenario may seem weird, but well, technically it can happen. Destructor'd better swap the current state of the pool to local variables and work on them. Repeating, if necessary.
That infinite loop in main() could spoil the destruction of global objects. Compiler could be smart enough to figure out that return is unreachable and skip the destruction part altogether.
pool_size could be a static member.

Constructors and Advanced Exception Throwing C++

I have to analyze this C++ code which involves exceptions, but I’m not used to analyzing what code is supposed to do. There’s several things about it I don’t understand. Additionally, I don’t have experience with a buffer data structure or advanced details of throwing exceptions in C++.
What is the max size of the data structure?
That is to say, if a Seq_Buffer is made with size_ = 100, how many
elements can be stored?
My intuition tells me that if you create something with size 100,
it’s max size will be 100, but I can’t be sure with facts.
Constructors can throw exceptions in c++?
Can the constructor for Seq_Buffer throw an exception?
Assuming this is true, why does that work without explicating
stating a try, catch, or throw block? I thought that was the only way to get exceptions
I’ve tried searching for these two questions, but I’m really quite lost. Thank you for taking the time to read.I’ve tried searching for these two questions, but I’m really quite lost. Thank you for taking the time to read.
The code for the Seq_Buffer class with its constructor is below:
#ifndef SEQBUFFER_H
#define SEQBUFFER_H
#include <memory>
#include <experimental/optional>
#include "optional.h"
// Exceptions classes
struct full_buffer {};
struct empty_buffer {};
// Sequential buffer for values of type T
template <typename T>
class seq_buffer {
public:
// Constructs a buffer for n elements
seq_buffer(int n) :
size_{n},
buf_{new item_type[size_]}
{
}
// Default destructor
~seq_buffer() = default;
// Size of buffer
int size() const noexcept {
return size_;
}
// Is buffer empty?
bool empty() const noexcept {
return next_read_ == next_write_;
}
// Is buffer full?
bool full() const noexcept {
const int next = next_position(next_write_);
return next == next_read_;
}
// Put element x into buffer with marker last.
// An empty element signals end of buffer.
void put(const optional<T> & x);
// Gets a pair with next element and last indication.
// Pair is accessed through members first and second
optional<T> get();
private:
// Compute next position after p following circular order
int next_position(int p) const noexcept {
return p + ((p+1>=size_)?(1-size_):1);
}
private:
// Size of buffer
const int size_;
using item_type = optional<T>;
// Unique pointer to buffer of size_ elements.
std::unique_ptr<item_type[]> buf_;
// Next position to read
int next_read_ = 0;
// Next position to write
int next_write_ = 0;
};
template <typename T>
void seq_buffer<T>::put(const optional<T> & x)
{
const int next = next_position(next_write_);
if (next == next_read_) throw full_buffer{};
if (!x) {
buf_[next_write_] = {};
}
else {
buf_[next_write_] = *x;
}
next_write_ = next;
}
template <typename T>
optional<T> seq_buffer<T>::get()
{
if (empty()) throw empty_buffer{};
auto res = buf_[next_read_];
next_read_ = next_position(next_read_);
return res;
}
#endif
Yes we can throw exceptions from constructors.It is the best way of dealing with constructor failures or class initialization error.Take this code example
class bar
{
public:
bar()
{
std::cout << "bar() called" << std::endl;
}
~bar()
{
std::cout << "~bar() called" << std::endl;
}
};
class foo
{
public:
foo()
: b(new bar())
{
std::cout << "foo() called" << std::endl;
throw "throw something";
}
~foo()
{
delete b;
std::cout << "~foo() called" << std::endl;
}
private:
bar *b;
};
int main(void)
{
try {
std::cout << "heap: new foo" << std::endl;
foo *f = new foo();
} catch (const char *e) {
std::cout << "heap exception: " << e << std::endl;
}
try {
std::cout << "stack: foo" << std::endl;
foo f;
} catch (const char *e) {
std::cout << "stack exception: " << e << std::endl;
}
return 0;
}
Here you are throwing exception from the constructor itself.But there are situations where you allocate memory(heap) in the constructor.In those situation it is not much useful to throw exception in the constructor as this will lead to memory leak.Because if the class fails to initialize then there will be no destructor called for the class as there is already exception thrown(assuming that in the destructor the allocated memory is freed using free).So throwing exception in constructor depends on the scenario or the use case.Not every situation benefits from throwing exception in the constructor.

Correct way to design data class to handle polymorphic data

I need to design a struct data which will hold pointer to Base data type. User should be able to easily create object of this data struct and pass around without handling much of memory management issues.
I have created few structures, please suggest the correct way to handle it.
struct BaseData {
enum DataType { DATATYPE_1, DATATYPE_2 };
virtual ~BaseData() { cout << "BaseData Dtor" << endl; }
};
struct DataType1 : BaseData {
virtual ~DataType1() { cout << "DataType1 Dtor" << endl; }
};
struct DataType2 : BaseData {
virtual ~DataType2() { cout << "DataType2 Dtor" << endl; }
};
struct Data {
Data() { cout << "Data Ctor" << endl; }
Data(const Data& o) {
if (o.baseData->type == BaseData::DATATYPE_1) {
baseData = new DataType1;
*(static_cast<DataType1*>(baseData)) = *(static_cast<DataType1*>(o.baseData));
}
else if (o.baseData->type == BaseData::DATATYPE_2) {
baseData = new DataType2;
*(static_cast<DataType2*>(baseData)) = *(static_cast<DataType2*>(o.baseData));
}
}
virtual ~Data() {
cout << "Data Dtor" << endl;
delete baseData; //here it results in segmentation fault if object is created on stack.
baseData = NULL;
}
BaseData* baseData;
};
vector <Data> vData;
void addData(const Data& d) { cout << "addData" << endl; vData.push_back(d); }
The client code looks like below.
int main()
{
{
DataType1 d1;
d1.type = BaseData::DATATYPE_1;
Data data;
data.baseData = &d1;
addData(data);
}
{
BaseData* d2 = new DataType2;
d2->type = BaseData::DATATYPE_2;
Data data;
data.baseData = d2;
addData(data);
delete d2;
d2 = NULL;
}
{
Data data;
data.baseData = new DataType1;
static_cast<DataType1*>(data.baseData)->type = BaseData::DATATYPE_1;
addData(data);
delete data.baseData;
data.baseData = NULL;
}
}
Code in block 1 and block 2 crashes due to double deletion. How can i handle all these use cases properly.
One way what I have thought of is, hide baseData pointer using private and provide a method to user setBaseData(const BaseData& o) in struct Data.
void setBaseData(const BaseData& o) {
cout << "setBaseData" << endl;
if (o.type == BaseData::DATATYPE_1) {
baseData = new DataType1;
*(static_cast<DataType1*>(baseData)) = static_cast<const DataType1&>(o);
}
else if (o.type == BaseData::DATATYPE_2) {
baseData = new DataType2;
*(static_cast<DataType2*>(baseData)) = static_cast<const DataType2&>(o);
}
}
With setBaseData() I am able to avoid segmentation fault and user is free to create object of struct Data in which ever he likes.
Is there any better way to design these classes?
Your problem is that you are trying to manage ownership by yourself. Instead you could use explicit ownership management using the unique_ptr type.
Assuming the same type definitions you used (+ The createDataType method we'll see later):
struct BaseData {
enum DataType { DATATYPE_1, DATATYPE_2 };
virtual ~BaseData() { cout << "BaseData" << endl; }
static std::unique_ptr<BaseData> createDataType(DataType type);
};
struct DataType1 : BaseData {
virtual ~DataType1() { cout << "DataType1" << endl; }
};
struct DataType2 : BaseData {
virtual ~DataType2() { cout << "DataType2" << endl; }
};
Notice that we are now using a factory for creating our objects, like so:
static std::unique_ptr<BaseData> BaseData::createDataType(BaseData::DataType type) {
switch(type) {
case BaseData::DATATYPE_1:
return std::make_unique<DataType1>();
case BaseData::DATATYPE_2:
return std::make_unique<DataType2>();
default:
throw std::runtime_error("ERR");
}
}
Then, you should declare your managing Data object as follows:
struct Data {
Data()
: baseData(nullptr) {}
Data(std::unique_ptr<BaseData> data)
: baseData(std::move(data)) {}
Data(Data && rhs)
: baseData(std::move(rhs.baseData)) {}
std::unique_ptr<BaseData> baseData;
};
And now we could write clean, clear and safe code as this:
vector<Data> vData;
void addData(Data&& d) {
if (dynamic_cast<DataType1 *>(d.baseData.get()) != nullptr)
cout << "Adding DataType 1" << endl;
else if (dynamic_cast<DataType2 *>(d.baseData.get()) != nullptr)
cout << "Adding DataType 2" << endl;
vData.push_back(std::move(d));
}
int main()
{
{ // Option 1: Create base data somewhere, create data from it
auto baseData = createDataType(BaseData::DATATYPE_1);
Data data { std::move(baseData) };
addData(std::move(data));
}
{ // Option 2: Create data directly knowing the base data type
Data data { createDataType(BaseData::DATATYPE_2) };
addData(std::move(data));
}
{ // Option 3: Create data and add it to the vector
addData({ createDataType(BaseData::DATATYPE_1) });
}
}
And you could always check for the actual type of the baseData using the same dynamic casts as in addData
Code in block 1 and block 2 crashes due to double deletion. How can i handle all these use cases properly.
By following the rule of 3 (or rule of 5 if you would like to support efficient move operations):
if a class defines one (or more) of the following it should probably explicitly define all three:
destructor
copy constructor
copy assignment operator
You've neglected implementing a custom copy assignment operator. The use of the default copy assignment operator results in the double deletion.
Also, never assign a pointer to an automatic variable to Data::baseData like you do here in block 1.
The destructor of Data will delete this pointer, which results in undefined behaviour.
Also, never delete the pointer owned by Data::baseData unless you're going to replace it with something else.
To avoid doing these by accident, I recommend declaring Data::baseData private as you've already considered.
Is there any better way to design these classes?
Yes. Don't ever use bare pointers to owned memory. Use std::unique_ptr instead.

unique_ptr does not call the destructor to free the pointer

I am passing unique_ptr to function and then move the pointer to another unique_ptr, all is working fine as need, but while point is unique_ptr does not call destructor of the when it goes out of scope.
Below is my code. and its output, the code is in eclipse.
#include <iostream>
#include <memory>
using namespace std;
class BaseCcExpander;
class DeriveHandler;
class ExpansionRuleExecuter;
class DeriveType1;
class ParamBase
{
public :
ParamBase()
{
std::cout << "Ctor:ParamBase:\n";
}
std::unique_ptr<ExpansionRuleExecuter> paramexpander;
virtual ~ParamBase() { std::cout << "Dtor::~ParamBase:\n"; }
virtual void attachBase(int paramGrp,int paramId,std::unique_ptr<ExpansionRuleExecuter> xbaseExpander);
};
ParamBase* obj;
void ParamBase::attachBase(int paramGrp,int paramId,std::unique_ptr<ExpansionRuleExecuter> xbaseExpander)
{
std::cout << "In: ParamBase::attachHandler :\n";
paramexpander = std::move(xbaseExpander);
}
class ExpansionRuleExecuter
{
public:
ExpansionRuleExecuter()
{
std::cout << "Ctor ExpansionRuleExecuter::ExpansionRuleExecuter:\n" << endl;
}
virtual ~ExpansionRuleExecuter(){
std::cout << "Dtor ~ExpansionRuleExecuter::ExpansionRuleExecuter:\n" << endl;
}
virtual void handleExpansion() = 0;
};
class DeriveHandler : public ExpansionRuleExecuter
{
public:
DeriveHandler()
{
std::cout << "Ctor::DeriveHandler:\n" << endl;
}
~DeriveHandler()
{
std::cout << "Dtor::~DeriveHandler:\n" << endl;
}
void handleExpansion()
{
std::cout << "DeriveHandler expanded\n" << endl;
}
};
ParamBase *obj1;
class BaseCcExpander
{
public:
BaseCcExpander()
{
std::cout << "Ctor::BaseCcExpander:\n" << endl;
}
virtual ~BaseCcExpander()
{
std::cout << "Dtor::~BaseCcExpander:\n" << endl;
}
typedef unique_ptr<ExpansionRuleExecuter> ccHandler;
BaseCcExpander::ccHandler ccBaseHandler;
void attachHandler(int paramGrp, int paramId,std::unique_ptr<ExpansionRuleExecuter> xhandler)
{
std::cout << "BaseCcExpander::attachHandler:\n" << endl;
obj1->attachBase(paramGrp,paramId,std::move(xhandler));
}
};
class DeriveType1 : public ParamBase
{
public :
DeriveType1() { std::cout << "Ctor: DeriveType--------1:\n" << endl;}
~DeriveType1() { std::cout << "Dtor::~DeriveType---------1\n" << endl;}
void attachBase(std::unique_ptr<ExpansionRuleExecuter> xbaseExpander);
};
BaseCcExpander ccexpander;
int main()
{
obj1 = new(DeriveType1);
ccexpander.attachHandler(1,2,std::unique_ptr<ExpansionRuleExecuter>(new DeriveHandler));
if(obj1->paramexpander.get())
{
ExpansionRuleExecuter *expand = obj1->paramexpander.get();
expand->handleExpansion();
}
}
You wrote in a comment:
but by is obj1 not destroying even after the program is over as its in the global space, it should destroy.
There is some misunderstanding here. obj1 is destroyed but the object it points to is not deleted when obj1 is destroyed. If the compiler did that, you won't be able to use:
int main()
{
int i = 10;
int* ip = &i;
// You don't want the run time to call the equivalent of
// delete ip;
// when the function returns. That will lead to undefined behavior
// since ip does not point to memory allocated from the heap.
}
When the program ends, the OS reclaims the memory used by the program but that does not mean that it calls the destructor of obj1.
Had the destructor been responsible for releasing resources other than memory, such as network connections, locks on shared files/folders, etc., they will not be released when the program ends without the destructor getting called.
Your variable pointed by obj1 is not deleted, hence its members would not be destroyed until the delete happens and the unique_ptr will remain alive then the destructor will never be called.
You should either call delete on obj1 at the end of your program or use an unique_ptr on it.

C++ confusion with new and scope

I am trying to learn C++ and from what I've read in books and on SO:
If I use auto x = new Object(); x is a pointer to address of Object and this is in dynamic memory and exists until I delete it.
However if I use Object x; or auto x = Object() it only lasts until it goes out of scope.
In an example they have shown this:
void foo()
{
Point p = Point(0,0);
} // p is now destroyed.
What I don't understand is what happens when I return a object when I don't use new? Will it be a copy of the object?
Here is an example of what I am not sure about:
class Object
{
public:
int X;
static Object Foo(int y)
{
Object result;
result.X = y;
return result;
}
};
class TestContainer
{
public:
void Run()
{
for(auto i = 0; i < 10; i++)
{
_objects.at(i) = Object::Foo(i + (rand() % 10 + 1));
}
}
private:
std::vector<Object> _objects;
};
void main()
{
TestContainer tc;
while(true)
{
tc.Run();
}
}
Note I haven't tested this code but I think it illiterates my confusion. In my main function I instantiate TestContainer and endless call it's Run method. This in turn loops calling a static Foo method on Object that returns a copy of a new Object, which is stored in a vector.
My question is, what happens with all the Object's? If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Will it be a copy of the object?
Yes.
Or a move could be used instead, or the entire thing could be optimised away to produce only one actual object in your final, compiled program.
But, basically, yes.
If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Yes.
As an aside, you're using at on elements that don't exist; to add elements, use insert or push_back.
A simple class like this behaves much like a POD variable. o1=o2 copies the fields, element-wise. So the target Object of an assignment does not get deleted but overwritten.
Objects which go out of scope "go away" (because the stack is unwound) like e.g. an int.
Here is a run-able example that I believe illustrates this behavior:
#include <iostream>
using namespace std;
class Foo {
private:
int id;
public:
Foo(int x)
: id(x)
{
cout << this->id << " is created" << endl;
}
Foo(const Foo& rhs)
: id(rhs.id)
{
cout << "copied " << this->id << endl;
}
Foo& operator=(Foo rhs){
this->id=rhs.id;
cout << "assigned " << this->id << endl;
return *this;
}
~Foo(){
cout << this->id << " is destroyed" << endl;
}
int getID(){
return this->id;
}
};
Foo bar(){
Foo f1 = Foo(1);
cout << f1.getID() << " from bar" << endl;
return f1;
}
int main(){
Foo f2 = bar();
cout << f2.getID() << " from main" << endl;
return 0;
}
This produces this output:
1 is created
1 from bar
1 from main
1 is destroyed
From this, I'm not seeing a copy or an assignment. I suspect what is happening is that both f1 and f2 are referencing the same instance of the object in memory. The object is not being de-allocated when the f1 reference goes out of scope because the object has another reference assigned to it.