Section 19.3 presents a string representation in a chapter whose main focus is operator overloading, specifically the special operators []
, ->
, and ()
. It implements copy_from()
as an ancillary function as follows:
void String::copy_from(const String &x)
// make *this a copy of x
{
if (x.sz <= short_max)
{
memcpy(this, &x, sizeof(x);
ptr = ch;
}
else
{
ptr = expand(x.ptr, x.sz+1);
sz = x.sz;
space = 0;
}
}
The class interface looks like this:
#ifndef STRING_EXERCISE_H
#define STRING_EXERCISE_H
namespace simple_string
{
class String;
char *expand(const char *ptr, int n);
}
class String
{
public:
String(); // default constructor x{""}
explicit String(const char *p); // constructor from C-style string
String(const String &s); // copy constructor
String &operator=(const String& s); // copy assignment
String(String &&s) // move constructor
String &operator=(String &&s) // move assignement
~String() // destructor
char &operator[](int n); // unchecked element access
char operator[](int n) const;
char &at(int n); // checked element access
char at(int n) const;
String &operator+=(char c) // add char c to the end
const char *c_str(); // c-style string access
const char *c_str() const;
int size() const; // number of elements
int capacity() const; // elements plus available space
private:
static const short short_max = 15;
int sz;
char *ptr;
union
{
int space; // unused allocated space
char ch[short_max+1]; // leave space for terminating 0
};
void check(int n) const; // range check
void copy_from(const String &x);
void move_from(String &x);
}
#endif
How can String::copy_from()
use memcpy()
to copy the class? I thought the class copied had to be trivially copyable (which it is not, because String
has user-defined constructors, copy operations, move operations, and destructors).
How can
String::copy_from()
usememcpy()
to copy the class?
int
, char
, and the anonymous union are all trivially copyable. So while you cannot perform a memcpy
of a String
, you can perform a memcpy
of its members. All of them, all at once. The technically correct code for this would be:
memcpy(&this->sz, &x.sz, sizeof(x));
That copies the range of memory of the storage for the members of this object.That is guaranteed by the rules of standard layout types. For standard layout types, the members are stored in definition order. So if you start with the first, and cover the range of all objects, that should copy the members.
However, the standard also says that the first member subobject of a standard layout type is required to have the same address as the object itself:
If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member.
That means &this->sz
must be the same address as this
, and &x.sz
must be the same address as &x
.
So just cut out the middle-man:
memcpy(this, &x, sizeof(x));
This is only allowed because of the rules of standard layout types.
A bigger issue is that copy_from never checks for self assignment. memcpy
doesn't work with overlapping memory ranges. Maybe operator=
and similar functions check for this already.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With