Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Do different compilers handle shared_ptr assignment differently?

I am running into an issue in my code where I get a memory access violation error when trying to set a member variable of type shared_ptr on class Node equal to another shared_ptr which is passed into the setter method for this member variable called setMarkerPtr. I created a minimal reproducible example but to my surprise the minimal example does actually compile.

In my minimal example the line nodePtr->setMarkerPtr(markerPtr) sets a member variable of the Node class called markerPtr_ to markerPtr which is a shared_pointer returned from a function called addMarkerToDraw. Draw stores as one of it’s members a vector of shared_ptr<Marker>.

#include "graph.h"
#include "draw.h"
#include "node.h"
#include "marker.h"
#include <memory>
#include <vector>
#include <iostream>

int main()
{
    std::unique_ptr<Graph> g = std::make_unique<Graph>(Graph()); 
    std::unique_ptr<Draw>  d = std::make_unique<Draw>(Draw());
    
    auto n = std::make_shared<Node>(Node());
    auto m = std::make_shared<Marker>(Marker(*n));
    auto nodePtr = insertNode(n,g->getNodes());
    auto markerPtr = addMarkerToDraw(m,d->getMarkers());
    std::cout << "address of markerPtr passed into setMarkerPtr(): " << markerPtr << std::endl;
    nodePtr->setMarkerPtr(markerPtr);
    return 0;
};

I have put up a minimal reproducible example using Git gist which i was able to compile without errors using g++ -pipe -02 -std=c++17 -o a.out smart_pointers.cpp marker.cpp graph.cpp draw/cpp node.cpp, but for brevity sake the main focus of the question is on this function:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

void Node::setMarkerPtr(SharedMarkerPtr m) {
    std::cout << "markerPtr_ before setting: " << markerPtr_ << std::endl; 
    markerPtr_ = m;
    std::cout << "markerPtr_ after setting: " << markerPtr_ << std::endl; 
};

In my minimal example, the member function markerPtr_ gets set to the shared_ptr which is passed in to the function. I verify this via the console out statements, checking to see if the memory addresses are the same before and after which gives:

address of markerPtr passed into setMarkerPtr(): 0x108df00
markerPtr_ before setting: 0
markerPtr_ after setting: 0x108df00

However, when I run the full code on my local machine I get an error in the same setter function Node::setMarkerPtr:

Exception thrown: write access violation.
_Right was 0x7FF7DF41D5F8.

Stepping through the code I can see that this error is thrown in this section of code on the line _Right = _STD move(_Tmp);:

#if _HAS_CXX17
template <class _Ty, enable_if_t<is_move_constructible_v<_Ty> && is_move_assignable_v<_Ty>, int> _Enabled>
#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv
template <class _Ty, int _Enabled>
#endif // _HAS_CXX17
_CONSTEXPR20 void swap(_Ty& _Left, _Ty& _Right) noexcept(
    is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_assignable_v<_Ty>) {
    _Ty _Tmp = _STD move(_Left);
    _Left    = _STD move(_Right);
    _Right   = _STD move(_Tmp);
}

Stepping back a little further, the code above is called from swap which is in turned called from the = operator.

Why does setting a shared_ptr member variable like this result in a memory access violation during the swap and is it compiler version related?

>Solution :

This may or may not be related to your crash (since you did not provide a Minimal, Reproducible Example on StackOverflow – please don’t host StackOverflow-related content on external sites)…

All of your calls to std::make_unique() and std::make_shared() are wrong. You are not supposed to call the class constructors yourself, only pass in the constructor parameters. The functions will call the constructors for you, eg:

//std::unique_ptr<Graph> g = std::make_unique<Graph>(Graph()); 
auto g = std::make_unique<Graph>(); 

//std::unique_ptr<Draw>  d = std::make_unique<Draw>(Draw());
auto d = std::make_unique<Draw>();    
    
//auto n = std::make_shared<Node>(Node());
auto n = std::make_shared<Node>();

//auto m = std::make_shared<Marker>(Marker(*n));
auto m = std::make_shared<Marker>(*n);

Each one of your original calls creates a temporary object, which then gets copied into another object that is then managed by unique_ptr/shared_ptr. At best, this is just inefficient.

However, in the last case, this may be significant, as we can’t see what Marker‘s constructor and destructor are actually doing. What does Marker do with the Node it is given? We don’t know. For all we know, destroying the temporary Marker could be inadvertently destroying the Node object that the copied Marker still depends on.

For that matter, you are dereferencing a Node* pointer when passing it to Marker‘s constructor. What does Marker do with that Node object? And more importantly, does Marker take in that Node by value or by reference? If the first case, then is Marker storing an internal reference to the Node object? If so, then it will be referencing a Node that gets destroyed immediately after the Marker constructor exits.

We just can’t see everything you are doing, which makes it hard to answer your question.

In any case, your crash implies that in main(), either nodePtr is not pointing at a valid Node object, or markerPtr is not pointing at a valid Marker object. Use a debugger to figure out which is the case.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading