I am trying to grasp the concept of move semantics, rvalues, lvalues in c++ and am facing a problem. I am first looking at this popular answer - https://stackoverflow.com/a/3109981/9576161
I wrote a small program based on that reply to understand what is going on. I am using g++ (on linux) and the -fno-elide-constructors for compiling without rvalue optimization by the compiler.
Here is the small program:
#include <iostream>
#include <cstring>
using namespace std;
class string {
public:
char* data;
string (const char* p) {
cout << "Constructor 1 called\n";
size_t size = strlen(p) + 1;
data = new char[size];
cout << "this location is: " << this << endl;
memcpy (data,p,size);
}
string (string&& that) {
cout << "Constructor 2 called\n";
//cout << "data is " << data << " data location is: " << &data << endl;
cout << "data location is: " << &data << endl;
cout << "that.data is " << that.data << " that.data location is: " << &that.data << endl;
data = that.data;
that.data = nullptr;
cout << "this location is: " << this << " data is: " << data << endl;
cout << "data location is: " << &data << endl;
cout << "that.data location is: " << &that.data << endl;
}
~string() {
delete[] data;
cout << "Destructor called for object located at: " << this << endl;
}
void print() {
cout << "String is: " << data << endl;
}
};
int main () {
::string R = "12345";
cout << "R constructed and located at: " << &R << endl << endl;
return 0;
}
On running the program, I see the following result:
ubuntu#thinkpad:~/programming_practice/c_projects$ g++ -fno-elide-constructors move_semantics_short.cpp
ubuntu#thinkpad:~/programming_practice/c_projects$ ./a.out
Constructor 1 called
this location is: 0x7fffac01bb80
Constructor 2 called
data location is: 0x7fffac01bb78
that.data is 12345 that.data location is: 0x7fffac01bb80
this location is: 0x7fffac01bb78 data is: 12345
data location is: 0x7fffac01bb78
that.data location is: 0x7fffac01bb80
Destructor called for object located at: 0x7fffac01bb80
R constructed and located at: 0x7fffac01bb78
Destructor called for object located at: 0x7fffac01bb78
ubuntu#thinkpad:~/programming_practice/c_projects$
Notice the line data = that.data in the second constructor (where it says "Constructor 2 is called"). What does it do? Aren't data and that.data both character pointers? Why is data not changing in value? Shouldn't data now equal the value of that.data which is 0x7fffac01bb80? Instead it seems like some kind of memcopy is occurring and the character string that that.data points to is now pointed to by data. Any hints on what is going on would be helpful.
The type of &data is char**. That is, it's the memory location of the pointer that stores the memory location of some char.
data = that.data; does not make &data equal &that.data. It just makes data and that.data equal. They now point to the same char, but they each exist independently in memory.
If instead of printing data's address, you print the address stored in data, you can see that you are stealing the array of chars that was owned by that in your move constructor.
Related
This question already has answers here:
cout << with char* argument prints string, not pointer value
(6 answers)
Closed 3 years ago.
If char* is a pointer (I find its size is always 4 bytes), how do I find its value (the address in hexa or decimal)? I tried &(*p) for char *p. It simply returned the initial string. If it is always 4 bytes, how is it that it can be initialized to long strings but point to the first character? In other words where is the string stored?
Is char* a weird pointer used for purposes other than what a pointer is intended to be?
// an initial pointer (usually its size is 32bit or 64bit, depending on CPU/OS).
// it's value is currently NULL (not pointing anywhere),
// so we can't do very much with this right now.
char* p = nullptr;
// for the sake of sanity, check the value (should be zero)
// we have to convert to intptr_t, otherwise we'd get the string
// value being printed out.
std::cout << "p address = " << intptr_t(p) << std::endl << std::endl;
// lets allocate a few chars to play with
p = new char[10];
// copy in some text value
std::strcpy(p, "Hello");
// and now if we print the address, the text string,
// and the char we are pointing at
std::cout << "p address = " << intptr_t(p) << std::endl;
std::cout << "p string = " << p << std::endl;
std::cout << "p dereferenced = " << *p << std::endl << std::endl;
// for fun, lets increment the pointer by 1
++p;
// this should have made a couple of changes here
std::cout << "p address = " << intptr_t(p) << std::endl;
std::cout << "p string = " << p << std::endl;
std::cout << "p dereferenced = " << *p << std::endl << std::endl;
// decrement again (so we can delete the correct memory allocation!)
--p;
// now free the original allocation
delete [] p;
// if we print again, notice it still has the memory location?
std::cout << "p address = " << intptr_t(p) << std::endl;
// This would be bad to access (we've just deleted the memory)
// So as a precaution, set the pointer back to null
p = nullptr;
// should be back where we started
std::cout << "p address = " << intptr_t(p) << std::endl;
I wrote a simple program to see how C++ handles pointers to string objects (new to OOP), and I was suprised to see that string* as which was assigned the memory address of string a, didn't store a value equivalent to &a. Also, the console didn't print the value to *as. Could this be an error on my end or the system, or am missing something fundamental here?
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
string a = "asdf";
string* as = &a;
string* as_holder = &a;
int main()
{
cout << "a = " << a << "\t" << "&a = " << &a << " *as = " << *as << endl
<< "as = " << as << endl
<< "++as = " << ++as << endl
<< "*as = " << *as << endl << endl;
return 0;
}
output:
a = asdf &a = 011ff68C *as =
as = 011FF6A8
++as = 011FF6A8
*as =
In my test of the valid portion of your program (the first two lines of cout), the printout showed the same address:
a = asdf &a = 0x8049c90 *as = asdf
as = 0x8049c90
(link to a demo)
Lines three and four, however, amount to undefined behavior: once you do ++as, you are moving the pointer to the next std::string in an "array of strings" (which does not exist). Therefore, the subsequent attempt at dereferencing as is undefined behavior.
If you would like to obtain a pointer to the data of your string, such that you could move to the next character by incrementing the pointer, you could use c_str() member function, like this:
const char *as = a.c_str();
as++;
cout << as << endl; // This would print "sdf"
I have the following code.
class Wave {
int m_length;
data_type * m_data;
public:
Wave(){
blah...blah...blah
m_data = NULL;
m_length = 0;
cout << "Wave " << this << " created on " << m_data << " with m_length " << m_length << endl;
}
Wave(int len, data_type data){
blah...blah...blah
if (len) {
m_length = len;
m_data = new data_type [m_length];
} else {
m_length = 0;
m_data = NULL;
}
cout << "Wave " << this << " created on " << m_data << " with m_length " << m_length << endl;
}
~Wave()
{
cout << "Wave " << this << " destructor on " << m_data << " started ";
if (m_length) delete[] m_data;
cout << "and finished " << endl;
};
Wave & operator+= (const Wave wave){
cout << __FUNCTION__ << ":" << __LINE__ << " m_length " << m_length << endl;
if (NULL != m_data){
data_type * tBuf = new data_type [m_length + wave.Length()];
copy (wave.begin(),wave.end(), copy (begin(),end(),iterator(tBuf)));
cout << "Wave " << this << " data on " << m_data << " moved onto " << tBuf;
delete[] m_data;
m_data = tBuf;
cout << " and deleted" << endl;
} else {
m_data = new data_type [wave.Length()];
copy (wave.begin(), wave.end(), begin());
cout << "Wave " << this << " data created on " << m_data << " of length " << wave.Length() << endl;
}
m_length += wave.Length();
cout << __FUNCTION__ << ":" << __LINE__ << " m_length " << m_length << endl;
return *this;
};
}
main(){
blah..blah..blah
Wave sample;
for (......) {
cout << pulseNum << "-th High part: " << pulse->first << endl;
Wave tSample(x,y);
blah.blah.blah
sample += tSample;
cout << endl << pulseNum++ << "-th Low part: " << pulse->second << endl;
tSample = Wave(a,b);
blah.blah.blah
sample += tSample;
}
}
Below is a log of execution this code
Wave 0x28fe34 created on 0 with m_length 0
0-th High part: 220
Wave 0x28fe54 created on 0xc60f00 with m_length 207
operator+=:211 m_length 0
Wave 0x28fe34 data created on 0xc610a8 of length 207
operator+=:230 m_length 207
Wave 0x28fe9c destructor on 0xc60f00 started and finished
0-th Low part: 320
Wave 0x28febc created on 0xc61250 with m_length 301
Wave 0x28febc destructor on 0xc61250 started and finished
operator+=:211 m_length 207
Wave 0x28fe34 data on 0xc610a8 moved to 0xc61250 and deleted
operator+=:230 m_length 508
Wave 0x28fee0 destructor on 0xc61250 started and finished
Wave 0x28fe54 destructor on 0xc61250 started and finished
Most strange thing for me is that destructor is called more times than contructor. Moreover, it was called for objects never costructed before but for the same data address.
How can it be?
Wave has a compiler-generated copy constructor, which you see no output for.
This copy constructor is called for example to construct the object which is the parameter of Wave & operator+= (const Wave wave).
If you want to simplify your code, just define a std::vector<data_type> m_data data member, instead of using raw pointers (data_type * m_data).
In this way, the compiler will automatically generate proper copy constructor, copy operator= (and also move semantics for C++11-compliant compilers) and destructor for you class (the automatically generated copy constructor, copy operator= and destructor will operate member-wise, e.g. the automatically generated copy constructor will call copy constructors for each data members of your class).
To allocate data, instead of:
m_data = new data_type [m_length];
use:
m_data.resize(m_length);
You can also get rid of m_length data member, since the std::vector knows its own size (you can access it via std::vector::size() method).
Note that std::vector stores all its elements in a single continuous memory area (like new[]); you can access the beginning of that area using &m_data[0].
I'm trying to store a lambda in an object system involving several layers of indirection. I'm using g++ 4.7.1.
Depending on how exactly I construct the (equivalent) objects, the lambda may or may not have the correct value.
Code:
#include <iostream>
#include <functional> // used for std::function
using namespace std; // TODO nope
typedef function<int()> intf;
struct SaveLambda {
const intf func;
SaveLambda(const intf& _func) : func(_func) {}
};
struct StoreSaved {
const SaveLambda* child;
StoreSaved(const SaveLambda& _child) : child(&_child) {
cout << "Before returning parent: " << child->func() << endl;
}
};
int main() {
const int ten = 10;
auto S = SaveLambda([ten](){return ten;});
cout << "No indirection: " << S.func() << endl << endl;
auto saved = StoreSaved(S);
cout << "Indirection, saved: " << saved.child->func() << endl << endl;
auto temps = StoreSaved ( SaveLambda([ten](){cout << "&ten: "<< &ten << endl; return ten;}) );
cout << "***** what. *****" << endl;
cout << "Indirection, unsaved: " << temps.child->func() << endl;
cout << "***** what. *****" << endl << endl;
cout << "ten still lives: " << ten << endl;
}
Compile as g++ -std=c++11 -Wall -o itest itest.cpp and run: notice the one line of output with a different value.
What am I doing wrong? I assumed that capture-by-value would, well, capture by value. (Observe most disconcertingly that the print in StoreSaved (line 15) produces the correct value, unlike line 34, despite these both referring to the same object. The only difference is adding another layer of indirection.)
This is wrong:
auto temps = StoreSaved(
/* This temporary value dies at the last semicolon! */
SaveLambda([ten](){cout << "&ten: "<< &ten << endl; return ten;})
);
StoreSaved then has a pointer to a nonexistent object. Using it is UB.
As already pointed out by others, the problem is that in temps you end with a pointer to a nonexistent SaveLambda struct, as it is a temporary.
You can keep a copy using a SaveLambda struct in StoreSaved, instead of a pointer:
struct StoreSaved {
const SaveLambda child;
StoreSaved(const SaveLambda& _child) : child(_child) {
cout << "Before returning parent: " << child.func() << endl;
}
};
You also have to change all the child->func() to child.func(), as you are not dealing with a pointer anymore.
I have a structure and i am trying to print the address of their member variables.
When tried to print the address of char member variable through &(f.c) i am not getting their address.
Here is the code:
struct foo
{
char c;
short s;
void *p;
int i;
};
int main()
{
cout << "Size of foo: " << sizeof(foo) << endl;
foo f;
cout << "Address of c: " << reinterpret_cast<void*>(&f.c) << endl;
cout << "Address of c: " << &(f.c) << endl;
cout << "Address of s: " << reinterpret_cast<void*>(&f.s) << endl;
cout << "Address of s: " << &(f.s) << endl;
cout << "Address of p: " << reinterpret_cast<void*>(&f.p) << endl;
cout << "Address of p: " << &(f.p) << endl;
cout << "Address of i: " << reinterpret_cast<void*>(&f.i) << endl;
cout << "Address of i: " << &(f.i) << endl;
return 1;
}
Output
/pp/cplus/bas ]$ ./a.out
Size of foo: 12
Address of c: 0xffbfe680
Address of c: //----------- &(f.c). Why this is empty..
Address of s: 0xffbfe682
Address of s: 0xffbfe682
Address of p: 0xffbfe684
Address of p: 0xffbfe684
Address of i: 0xffbfe688
Address of i: 0xffbfe688
just want to know why it is not printing when i tried accessing it through &(f.c)
Compiled using gcc version 3.4.6
cout has an operator<< overload for char* which treats the argument like a pointer to a C-string and it tries to print all the characters in that C-string until it gets to a NUL (0) byte. To get around this behaviour, you have to cast the addresses to void* like you are doing every other line.
You have just experienced the reason that arrays are sometimes considered second-class data types because they are treated specially in some situations (i.e. arrays of char are treated differently by some things but not others).
The Address of c: is empty because that's what you get when you try to print a string pointed to by &f.c. As dark_charlie pointed out, using an uninitialised variable is undefined behaviour, so technically anything can happen, but the former is probably the explanation for what you're seeing (though we can only guess).
The reason is that without the reinterpret cast &(f.c) is a char* pointer which is treated as a string by cout. Because you haven't filled the char with anything, you invoke an undefined behavior (i.e. it can print anything).