Reputation: 3934
I intended to have a constructor to accept references and then create pointers to the objects passed as references and store these pointers in a field. However, for some reason the way I did it, copies are being created, and I don't see why:
#include <iostream>
#include <vector>
#include <memory>
// the class with the field of pointers
template<class T, class... Types>
class C
{
private:
std::vector<std::shared_ptr<T>> mem; // the field of pointers
public:
C(T& t, Types&... args)
// make pointers to the objects passed by reference and store them in mem
: mem{ std::make_shared<T>(t), std::make_shared<T>(args)... }
{
// to demonstrate that the pointers point to copies, alter one of the objects (subscript operator expected)
(*mem[0])[0] = 10;
// demonstrate the effect on the local copies
std::cout << "vectors in mem:" << "\n";
for (const auto& x : mem) {
for (const auto& y : *x) {
std::cout << y << ' ';
}
std::cout << "\n";
}
}
};
int main()
{
std::vector<int> v1{ 1, 2, 3 };
std::vector<int> v2{ 1, 2, 3 };
std::vector<int> v3{ 1, 2, 3 };
// make an object of type C with some vectors to store pointers to in field mem
C<std::vector<int>, std::vector<int>, std::vector<int>> c(v1, v2, v3);
// demonstrate that original vectors are unaltered
std::cout << "original vectors:"<< "\n";
for (const auto& y : v1) {
std::cout << y << ' ';
}
std::cout << "\n";
for (const auto& y : v2) {
std::cout << y << ' ';
}
std::cout << "\n";
for (const auto& y : v3) {
std::cout << y << ' ';
}
std::cout << "\n";
}
What am I missing here? Where is the copying happening and why?
Upvotes: 2
Views: 2199
Reputation: 42984
std::make_shared<T>
allocates a single chunk of memory large enough to store the object of type T (initializing it with the passed parameters, if any), and attached to it a control block to manage the shared ownership (like keeping the object reference count, etc.).
+---------------+
| Control block |
| |
+ ............. +
| Object (T) |
| |
+---------------+
So, even if you pass a reference to an object, make_shared
will create its own "deep" copy (constructing its object copy in place in a section of the memory block allocated as described above).
Upvotes: 2
Reputation: 40100
What am I missing here? Where is the copying happening and why?
To answer the question literally, from cppreference.com:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
Constructs an object of typeT
and wraps it in astd::shared_ptr
usingargs
as the parameter list for the constructor ofT
.
In your case, std::make_shared<T>(t)
(t
being a T&
) invokes T::T(T const&)
and builds a copy of t
. An incorrect solution would be to create your std::shared_ptr
s yourself without the help of std::make_shared
... but in this case you would create smart pointers to potentially statically allocated objects. And this is bad, like Undefined Behaviour bad.
A better solution is to simply avoid creating smart pointers to objects passed by reference, as it may come to a surprise to the caller. Better take smart pointers:
C(std::shared_ptr<T> t, std::shared_ptr<Types> ...args) { /* ... */ }
Upvotes: 2
Reputation: 170153
The reason is std::make_shared<T>(t)
. make_shared
will call the constructor of T
that accepts the parameters it's being given. And you give it a T
lvalue by reference.
Naturally the copy constructor is called. And it's a good thing. Had you created a shared pointer to the very object you are passed, your code will probably be a heap of undefined behavior.
I suggest you accept the parameter by smart pointer instead. It makes the ownership semantics clear to the user of your class from the API itself.
Upvotes: 2