How does shared_ptr increase counter when passed by value? - c++

I have this sample code below. I know little bit about RVO (return value optimization) and how copy constructor and assignment operator are skipped during the optimization and return of the value is placed directly on the memory on the left. So if shared pointer does the RVO how does the shared pointer know when to increase its counter? Because for some reason I thought shared pointer class would know when to increase counter based on the number copies or assignment it made.
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
A(){}
A(const A& other){ std::cout << " Copy Constructor " << std::endl; }
A& operator=(const A&other){
std::cout << "Assingment operator " << std::endl;
return *this;
}
~A(){
std::cout << "~A" << std::endl;
}
};
std::shared_ptr<A> give_me_A(){
std::shared_ptr<A> sp(new A);
return sp;
}
void pass_shared_ptr_by_val(std::shared_ptr<A> sp){
std::cout << __func__ << ": count sp = " << sp.use_count() << std::endl;
std::shared_ptr<A> sp1 = sp;
std::cout << __func__ << ": count sp = " << sp.use_count() << std::endl;
std::cout << __func__ << ": count sp1 = " << sp1.use_count() << std::endl;
}
void pass_shared_ptr_by_ref(std::shared_ptr<A>& sp){
std::cout << __func__ << ": count sp = " << sp.use_count() << std::endl;
std::shared_ptr<A> sp1 = sp;
std::cout << __func__ << ": count sp = " << sp.use_count() << std::endl;
std::cout << __func__ << ": count sp1 = " << sp1.use_count() << std::endl;
}
int main(){
{
shared_ptr<A> sp3 = give_me_A();
std::cout << "sp3 count = " << sp3.use_count() << std::endl;
pass_shared_ptr_by_val(sp3);
pass_shared_ptr_by_ref(sp3);
}
return 0;
}
output:
sp3 count = 1
pass_shared_ptr_by_val: count sp = 2
pass_shared_ptr_by_val: count sp = 3
pass_shared_ptr_by_val: count sp1 = 3
pass_shared_ptr_by_ref: count sp = 1
pass_shared_ptr_by_ref: count sp = 2
pass_shared_ptr_by_ref: count sp1 = 2
~A

If there is no copy, nothing needs to be counted.
If RVO is in play no copy is made, so why would the ref-count need to be increased? There is no extra object to destroy and decrement the ref-count.

Related

output to screen terminates when I try to access array in C++ [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 12 months ago.
I'm trying to return a pointer, that points to an array of strings, from a function and then access elements in the array that the returned pointer points to. I seem to get a pointer back from the function, but when I try to output individual elements of the array to the computer screen, my program seems to just stop outputting and end the program.
Running the code below, the pointer to the array outputs as expected (see screen output screenshot). However, when I try to access the array's elements, none of the elements are output to the screen and the program just ends. Any ideas/thoughts would be greatly appreciated. FYI, I realize that I'm not using the data that's being passed to the function, but that's because I'm trying to figure out how to access the data that gets returned first.
roster.h
#ifndef ROSTER_H
#define ROSTER_H
#include <iostream>
#include <string>
class Roster {
public:
std::string* parseData(const std::string Data);
};
#endif
roster.cpp
#include <iostream>
#include <string>
#include "roster.h"
std::string* Roster::parseData(const std::string Data) {
std::string stringData[3] = { "abc", "def", "ghi" };
return stringData;
}
rostertest.cpp
#include <iostream>
#include <string>
#include "roster.h"
void rosterClassTest()
{
Roster roster;
std::string* returnData = roster.parseData("red,blue,black,gray");
std::cout << "pointer returned: "
<< returnData << std::endl << std::endl;
std::cout << "element 0 of array: "
<< returnData[0] << std::endl << std::endl;
std::cout << "element 1 of array: "
<< returnData[1] << std::endl << std::endl;
std::cout << "element 2 of array: "
<< returnData[2] << std::endl << std::endl;
}
Roster::parseData() is returning a pointer to a local array variable that goes out of scope upon exit, destroying the array, and thus the function is returning a dangling pointer to invalid memory.
You should return a std::vector<std::string> (or std::array<std::string, 3>) instead, eg:
std::vector<std::string> Roster::parseData(const std::string Data) {
std::vector<std::string> stringData{ "abc", "def", "ghi" };
return stringData;
}
std::vector<std::string> returnData = roster.parseData("red,blue,black,gray");
std::cout << "pointer returned: " << returnData.data() << std::endl << std::endl;
for(size_t i = 0; i < returnData.size(); ++i) {
std::cout << "element " << i << " of array: " << returnData[i] << std::endl << std::endl;
}
If that is not an option, then you will have to new[] the array, and then the caller will have to delete[] the array when finished using it, eg:
std::string* Roster::parseData(const std::string Data) {
std::string *stringData = new std::string[3];
stringData[0] = "abc";
stringData[1] = "def";
stringData[2] = "ghi";
return stringData;
}
std::string* returnData = roster.parseData("red,blue,black,gray");
std::cout << "pointer returned: " << returnData << std::endl << std::endl;
std::cout << "element 0 of array: " << returnData[0] << std::endl << std::endl;
std::cout << "element 1 of array: " << returnData[1] << std::endl << std::endl;
std::cout << "element 2 of array: " << returnData[2] << std::endl << std::endl;
delete[] returnData;
In which case, you should return a std::unique_ptr instead, eg:
std::unique_ptr<std::string[]> Roster::parseData(const std::string Data) {
//std::unique_ptr<std::string[]> stringData(new std::string[3]);
auto stringData = std::make_unique<std::string[]>(3);
stringData[0] = "abc";
stringData[1] = "def";
stringData[2] = "ghi";
return stringData;
}
auto returnData = roster.parseData("red,blue,black,gray");
std::cout << "pointer returned: " << returnData.get() << std::endl << std::endl;
std::cout << "element 0 of array: " << returnData[0] << std::endl << std::endl;
std::cout << "element 1 of array: " << returnData[1] << std::endl << std::endl;
std::cout << "element 2 of array: " << returnData[2] << std::endl << std::endl;

Returning an std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> from a function results in weirdness

I'm having trouble understanding the (to me intricate) mechanisms executed behind the scenes in the following code example:
#include <utility>
#include <memory>
#include <iostream>
struct A {int a;};
struct B {int b;};
std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> FuncA() {
std::shared_ptr<A> a = std::make_shared<A>();
std::unique_ptr<B> b = std::make_unique<B>();
a->a = 12; b->b = 13;
std::cout << "FuncA a: " << a.get() << std::endl;
std::cout << "FuncA b: " << b.get() << std::endl;
std::cout << "FuncA a.a: " << a->a << std::endl;
std::cout << "FuncA b.b: " << b->b << std::endl;
return {a,b};
}
void FuncC(std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> input) {
std::cout << "FuncC a: " << input.first.get() << std::endl;
std::cout << "FuncC b: " << input.second.get() << std::endl;
std::cout << "FuncC a.a: " << input.first->a << std::endl;
std::cout << "FuncC b.b: " << input.second->b << std::endl;
}
void FuncB() {
auto ret = FuncA();
std::cout << "FuncB a: " << ret.first.get() << std::endl;
std::cout << "FuncB b: " << ret.second.get() << std::endl;
std::cout << "FuncC a.a: " << ret.first->a << std::endl;
std::cout << "FuncC b.b: " << ret.second->b << std::endl;
FuncC(ret);
}
int main(){
FuncB();
}
I've compiled the code with both GCC and Clang which give similar results:
FuncA a: 0xfeaec0
FuncA b: 0xfeaed0
FuncA a.a: 12
FuncA b.b: 13
FuncB a: 0xfeaec0
FuncB b: 0x7ffd1c8e4a00
FuncC a.a: 12
FuncC b.b: 479087264
FuncC a: 0xfeaec0
FuncC b: 0x406100
FuncC a.a: 12
FuncC b.b: 1449378512
As is clearly visible, the address of the std::unique_pointer reference (and of course also its value) are not the same as within FuncA, but the address and value of the std::shared_pointer are unchanged.
What's happening here, and what (if anything) could be done to make the reference-passing correct?
Is some form of copy-constructor being executed on the std::unique_ptr as a result of returning from FuncA?
std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> FuncA() {
// ...
std::unique_ptr<B> b = std::make_unique<B>();
// ...
return {a,b};
}
A local std::unique_ptr<B> is created and a reference to it is returned as the second element in the pair. This is a dangling reference and is later accessed, giving the program undefined behaviour.

std::move triggered destructor?

I'm adapting to an interface with parameter const vector<pair<int32_t, A>>& as.
To avoid multi-construct and multi-destruct, I write CreateAs as follow.
I was expecting it to trigger create move del each for once, but it turned out triggered move and del for twice.
What's the reason?
I made this way so that the A objects could destroy themselves automatically, even with new but without delete. Am I doing it right?
To reproduce it: https://repl.it/#unix1/ShrillFirsthandAdvance
#include <iostream>
#include <vector>
using namespace std;
struct A {
A() { cout << "A()" << endl; }
A(int32_t a) : a_(a) { cout << "Create A: " << a << endl; }
A(const A& oa) { a_ = oa.a_; cout << "Copy A: " << oa.a_ << endl; }
A(A& oa) { a_ = oa.a_; cout << "Copy A non-const: " << oa.a_ << endl; }
A(const A&& oa) { a_ = oa.a_; cout << "Move A: " << oa.a_ << endl; }
A(A&& oa) { a_ = oa.a_; cout << "Move A non-const: " << oa.a_ << endl; }
~A() { cout << "Del A: " << a_ << ", ptr: " << this << endl; }
int32_t a_;
};
void CreateAs(vector<pair<int32_t, A>>& as) {
as.reserve(3);
for (int32_t i = 0; i < 3; ++i) {
A* a = new A(i*i);
cout << "a ptr: " << &a << endl;
cout << "-----before insert----" << endl;
as.emplace_back(make_pair(i, move(*a)));
cout << "-----after insert-----" << endl;
}
}
void Test() {
vector<pair<int32_t, A>> as;
cout << "-----Create begin----" << endl;
CreateAs(as);
cout << "-----Create end------" << endl;
for (const auto& item : as) {
cout << item.first << "->" << item.second.a_ << endl;
}
}
int main(int32_t argc, char* argv[]) {
Test();
cout << "____end test____" << endl;
return 0;
}
make_pair constructs an A in a pair. emplace_back moves the pair and therefore A into the vector. The moved from pair is destroyed, also destroying the contained A.
To avoid any move, you might do
void CreateAs(std::vector<std::pair<int32_t, A>>& as) {
as.reserve(3);
for (int32_t i = 0; i < 3; ++i) {
as.emplace_back(i, i*i);
}
}
Demo
You currently have extra move with your extra make_pair.

Problems with c++ lifetime extension

I've tried to understand the semantics of c++ temporary objects lifetime extension. I've tried to simulate simple situation and was a bit surprised.
Below I'm providing my code.
#include <iostream>
struct C
{
C(const int new_a) { a = new_a; };
int a = 0;
};
C return_num()
{
C num(20);
std::cout << "From func(): num = " << num.a << ", by adress: " << &num.a << std::endl;
return num;
}
void pass_num(const C& num)
{
std::cout << "From func(): num = " << num.a << ", by adress: " << &num.a << std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main(): num = " << ext_num.a << ", by adress: " << &ext_num.a << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main(): num = " << num.a << ", by adress: " << &num.a << std::endl;
pass_num(num);
}
}
Here is the main question: return_num() works curiously from my point of view, cause I expected that address of the variable, which I'm trying to output in main, would be the same as internally in return_num(). Could you please explain me why it is not?
For example in pass_num() output address matches the external address which I got in main.
Here is example output:
Lifetime extention:
From func(): num = 20, by adress: 0x7fff44fc8b4c
From main(): num = 20, by adress: 0x7fff44fc8b70
Passing by reference:
From main(): num = 20, by adress: 0x7fff44fc8b6c
From func(): num = 20, by adress: 0x7fff44fc8b6c
Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.) rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state.
Please see Move Constructor
I changed the below in your code and I hope it is working as expected. I changed int a to int* a
#include <iostream>
class C
{
public:
int *a;
C( int new_a)
{
a = new int();
*a = new_a;
};
C(const C& rhs) { std::cout << "Copy " << std::endl; this->a = rhs.a; }
C(C&& rhs):a(std::move(rhs.a))
{
std::cout << "Move!!" <<"Address resource a " << &(*a) << ", Address of
resource rhs.a" << &(*rhs.a) << std::endl; rhs.a = nullptr;
std::cout << "Value of a:: " << *a << std::endl;
}
};
C return_num()
{
C num(20);
std::cout << "From return_num(): num = " << *num.a << ", Address of resource a :
"<< &(*num.a)<< std::endl;
return (std::move(num));
}
void pass_num(const C& num)
{
std::cout << "From pass_num(): num = " << *num.a << ", by adress: " << &num.a <<
std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main() 1 : num = " << *(ext_num.a) << ", by resource
adress: " << &(*ext_num.a) << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main() 2 : num = " << *num.a << ", by adress: " << &num.a
<< std::endl;
pass_num(num);
}
return 0;
}
The above code produces below output:
Lifetime extention:
From return_num(): num = 20, Address of resource a : 0x7fffeca99280
Move!!Address resource a 0x7fffeca99280, Address of resource rhs.a0x7fffeca99280
Value of a:: 20
From main() 1 : num = 20, by resource adress: 0x7fffeca99280
Passing by reference:
From main() 2 : num = 20, by adress: 0x7ffff466f388
From pass_num(): num = 20, by adress: 0x7ffff466f388
I hope it helps!
Imagine this function:
int getNumber(){
int num = 10;
return num;
}
This function does not return num as an object, it returns a no-named copy of it (r-value, if you will) with the same value. Therefore, it has a different address.
The same thing happens with your return_num function.
I suspect that taking the address of a member is inhibiting the optimization because the compiler doesn't know how to deal with all the possible edge cases. Eliminating taking the address of the member makes the optimization work.
#include <iostream>
struct C
{
C(const int new_a) { a = new_a; };
int a = 0;
struct C* t = this;
};
C return_num()
{
C num(20);
std::cout << "From func(): num = " << num.a << ", by adress: " << num.t << std::endl;
return num;
}
void pass_num(const C& num)
{
std::cout << "From func(): num = " << num.a << ", by adress: " << num.t << std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main(): num = " << ext_num.a << ", by adress: " << ext_num.t << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main(): num = " << num.a << ", by adress: " << num.t << std::endl;
pass_num(num);
}
}
Lifetime extention:
From func(): num = 20, by adress: 0x7ffd61f48a50
From main(): num = 20, by adress: 0x7ffd61f48a50
Passing by reference:
From main(): num = 20, by adress: 0x7ffd61f48a90
From func(): num = 20, by adress: 0x7ffd61f48a90

VS 2012 intellisense - function may not be initialized

I'm just playing around a little bit with decltype and noticed the intellisense in VS 2012 is giving me a error. This is the first time I have encountered this and the code still compiled.
#include <iostream>
int func(int param)
{
std::cout << "IM BEING CALLED: " << param << std::endl;
return 0;
}
int main()
{
auto& y = func;
auto z = func;
decltype((func))& x = func;
decltype((func)) k = func; // function 'k' may not be initialized but compiles
func(9);
x(10);
y(11);
z(12);
k(13);
std::cout << std::endl;
std::cout << "Address of func: " << func << std::endl;
std::cout << "Address of x: " << x << std::endl;
std::cout << "Address of y: " << y << std::endl;
std::cout << "Address of z: " << z << std::endl;
std::cout << "Address of k: " << k << std::endl;
std::cin.get();
return 0;
}
This is not a major problem nor interesting for most people but I was just wondering if anyone knows the reason behind the error?
I was just wondering if anyone knows the reason behind the error
It's just a parsing bug. Nothing more, nothing less.