physicalattraction
physicalattraction

Reputation: 6858

What is the correct syntax to construct a class with a shared_ptr as member field?

Suppose, I want to make a class B with a pointer to another class A. In the constructor of A I want to construct B and passing it a pointer to this. In principle, this pointer can be a shared_ptr, correct? Then how do I make such a class with the assignment of the shared_ptr in the constructor?

I tried it in the code below. ClassB and ClassC are identical, with a pointer to ClassA which constructs instances of ClassB and ClassC. The only two differences are that ClassB holds a regular pointer and ClassC holds a shared_ptr to ClassA and that the code with ClassB is working and the code with ClassC is not. What am I doing wrong?

// Main.cpp : Defines the entry point for the console application.

#include "ClassA.h"
#include <iostream>

int main(int argc, char* argv[])
{
    std::cout << "Create B and C separately, without a reference to A:" << std::endl;
    ClassB(nullptr);
    ClassC(nullptr);

    std::cout << "Create A, and through it B and C:" << std::endl;
    ClassA A;

    A.PrintHello();
    A.getObjectB().getPointerToA()->PrintHello();

    A.PrintHello();
    A.getObjectC().getPointerToA()->PrintHello();
    return 0;
}

// ClassA.h

#pragma once

#include "ClassB.h"
#include "ClassC.h"

class ClassA
{
private:
    ClassB objectB;
    ClassC objectC;

public:
    ClassA(void);

    ClassB getObjectB() { return objectB; };
    ClassC getObjectC() { return objectC; };

    void PrintHello();
};

// ClassA.cpp

#include "ClassA.h"
#include <iostream>
#include <memory>

ClassA::ClassA(void) : objectB(ClassB( this )), objectC(ClassC( std::make_shared<ClassA>(*this) ))
{
    std::cout << "Class A fully constructed" << std::endl;
}

void ClassA::PrintHello()
{
    std::cout << "Hello" << std::endl;
}

// ClassB.h

#pragma once

#include <memory>

class ClassA;

class ClassB
{
private:
    ClassA* pointerToA;

public:
    ClassB(ClassA* pA);

    ClassA* getPointerToA() { return pointerToA; };
};

// ClassB.cpp

#include "ClassB.h"
#include <iostream>

ClassB::ClassB(ClassA* pA) : pointerToA(pA)
{
    std::cout << "Class B constructed" << std::endl;
}

// ClassC.h

#pragma once

#include <memory>

class ClassA;

class ClassC
{
private:
    std::shared_ptr<ClassA> pointerToA;

public:
    ClassC(std::shared_ptr<ClassA> pA);

    std::shared_ptr<ClassA> getPointerToA() { return pointerToA; };
};

// ClassC.cpp

#include "ClassC.h"
#include <iostream>

ClassC::ClassC(std::shared_ptr<ClassA> pA) : pointerToA(pA)
{
    std::cout << "Class C constructed" << std::endl;
}

Upvotes: 2

Views: 331

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171293

These are not doing the same thing:

ClassA::ClassA(void) : objectB(ClassB( this )), objectC(ClassC( std::make_shared<ClassA>(*this) ))

The first initializer creates a temporary of type ClassB with the this pointer and initializes objectB by copying that temporary:

objectB(ClassB( this ))

The second creates a new ClassA as a copy of *this, stores it in a shared_ptr, then initializes a temporary ClassC with that shared_ptr, then initializes objectC by copying that temporary:

objectC(ClassC( std::make_shared<ClassA>(*this) ))

Your syntax is unnecessarily verbose, avoid the temporaries and copies and initialize your members directly:

    objectB( this ), objectC( std::make_shared<ClassA>(*this) )

This is equivalent to:

    objectB( this ), objectC( std::shared_ptr<ClassA>( new ClassA(*this) ) )

It should be clear that objectB has a pointer to this but objectB has a (shared) pointer to a different object, that is a copy of *this.

The point of a shared_ptr is it owns the pointer you give it, and will (generally) delete the pointer. You can't have a shared_ptr that owns this in an object's constructor, because until the object has finished being constructed it can't be owned by any shared_ptr (the pointer you give to a shared_ptr is a pointer to a complete object, not a partially constructed one half way through it's constructor) so there's no safe way to get a shared_ptr referring to this that you can pass to objectC's constructor. There is a way to do it, using the aliasing feature of shared_ptr but I think you should re-examine your design and ask why you want objectC to "own" the object it's part of ... that doesn't make sense.

Upvotes: 4

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

std::make_shared allocates and initializes a new object.

Upvotes: 1

Related Questions