拷贝构造函数第一个参数是一个类类型的引用,其他的参数都有默认值。
当拷贝初始化发生时,该拷贝初始化需要拷贝构造函数或移动构造函数。
1.拷贝初始化当使用=时会发生。
2.将一个对象作为实参传递给一个非引用类型的形参
3.从一个返回类型为非引用类型的函数返回一个对象
4.用花括号列表初始化一个数组中的元素或一个聚合类中的成员。
我们需要调用拷贝构造函数,但是永远都不会成功,因为其自身的参数也是非引用类型,为了调用它,必须拷贝其实参,而为了拷贝其实参,又需要调用拷贝构造函数,也就是自身,从而造成死循环。
当我们拷贝一个StrBlob时,shared_ptr指针的use_count会加1。拷贝一个StrBlobPtr没有变化。
class HasPtr{
public:
HasPtr(const string &s = string()):ps(new string(s)), i(0){ }
HasPtr(const HasPtr &hp):ps(new string(*hp.ps)),i(hp.i) { }
private:
string *ps;
int i;
};
拷贝运算符就是一个operator=的一个函数,返回一个本对象的引用。
在类对象发生赋值时使用拷贝赋值运算符。
它会将右侧运算对象的每个非static成员赋予左侧运算符对应成员。
在一个类没有定义自己的拷贝赋值运算符时,编译器会为它生成一个合成拷贝赋值运算符。
对于StrBlob,shared_ptr的use_count会加一。赋值给StrBlobStr不会加一。
public:
HasPtr(const string &s = string()):ps(new string(s)), i(0){ }
HasPtr(const HasPtr &hp):ps(new string(*hp.ps)),i(hp.i) { }
HasPtr& operator= (const HasPtr& hp){
ps = new string(*hp.ps);
i = hp.i;
}
private:
string *ps;
int i;
};
class HasPtr{
public:
HasPtr(const string &s = string()):ps(new string(s)), i(0){ }
HasPtr(const HasPtr &hp):ps(new string(*hp.ps)),i(hp.i) { }
HasPtr& operator= (const HasPtr& hp){
ps = new string(*hp.ps);
i = hp.i;
return *this;
}
string get_ps() const {
return *ps;
}
private:
string *ps;
int i;
};
析构函数是用来释放对象使用资源,并摧毁对象的非static数据成员的一个函数。
在类没有自己定义析构函数的时候,这个类会自己合成析构函数。
shared_ptr的use_count将会减少,当use_count为0时便会被释放。
对象被销毁时,weak_ptr所指向的内存不会被释放。
~HasPtr(){delete ps;}
bool fcn(const Sales_data *trans, Sales_data accum)
{
Sales_data item1(*trans), item2(accum);
return item1.isbn() != item2.isbn();
}
accum item1 和 item2三次。
三个相同的数。
构造的时候给了不同的值,输出了三个不同的结果。
不会改变输出结果,如果是默认拷贝构造函数的话。
struct numbered{
int mysn;
};
void f(numbered s){cout<<s.mysn<<endl;}
int main(){
numbered a, b = a, c = b;
f(a), f(b), f(c);
return 0;
}
//输出三个0
struct numbered{
int mysn;
numbered():mysn(0){}
numbered& operator= (numbered &n){mysn = n.mysn + 1; return *this;}
numbered(numbered& n):mysn(n.mysn + 1){}
};
void f(numbered s){cout<<s.mysn<<endl;}
int main(){
numbered a, b = a, c = b;
f(a), f(b), f(c);
return 0;
}
//输出为1,2,3
struct numbered{
int mysn;
};
void f(const numbered & s){cout<<s.mysn<<endl;}
int main(){
numbered a, b = a, c = b;
f(a), f(b), f(c);
return 0;
}
//输出为三个0
class Employee{
private:
string name;
static int jishu;
int id;
public:
Employee();
Employee(const string &name):name(name){id = jishu++;}
};
Employee::Employee() {{id = jishu++;}}
int Employee::jishu = 0;
int main(){
Employee e1("meng");
Employee e2("xiang");
Employee e3("zhi");
return 0;
}
不需要,因为没有实际意义。
当拷贝和赋值时,shared_ptr指向的对象的的use_count会加1。其他成员变量值会进行拷贝。销毁时,shared_ptr指向的对象的的use_count会减1。
不用,合成版本就能完成任务了。
如果没有定义析构函数,将会删除不了动态内存,发生内存泄漏。如果没有定义拷贝构造函数,那么指针将传递地址值,行为就不像值了变得像指针。
类值版本要为shared_ptr型的指针赋值时重新new一个动态内存。因为如果shared_ptr指向的对象消失等到use_count的值为0的时候,内存会自动释放。
class StrBlob1{
public:
typedef std::vector<std::string>::size_type size_type ;
StrBlob1();
StrBlob1(StrBlob1& b):data(std::make_shared<std::vector<std::string>>(*data)){}
StrBlob1 operator=(StrBlob1& sb1){auto d = std::make_shared<std::vector<std::string>>(*sb1.data); data = d;}
StrBlob1(std::initializer_list<std::string> il);
size_type size(){return data->size();}
bool empty() const {return data->empty();}
void push_back(const std::string &t){data->push_back(t);}
void pop_back();
std::string& front();
std::string& back();
const std::string &front() const;
const std::string &back() const;
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const;
};
class HasPtr{
public:
HasPtr(const string &s = string()):ps(new string(s)), i(0){ use = new size_t(0);}
HasPtr(const HasPtr &hp):ps(new string(*hp.ps)),i(hp.i),use(hp.use) {++(*use); }
HasPtr& operator=(const HasPtr& hp){
if(--*use == 0){
delete ps;
delete use;
}
ps = hp.ps;
i = hp.i;
use = hp.use;
++(*use);
return *this;
}
~HasPtr(){
if(--*use == 0){
delete ps;
delete use;
}
}
private:
string *ps;
int i;
size_t *use;
};
class TreeNote{
private:
string value;
int* count;
TreeNote *left;
TreeNote *right;
public:
TreeNote():value(""), count(new int(1)),left(nullptr), right(nullptr){}
TreeNote(TreeNote &tn){
++*tn.count;
if (--*count == 0){
delete count;
delete left;
delete right;
}
count = tn.count;
value = tn.value;
left = tn.left;
right = tn.right;
}
~TreeNote() {
if (--*count == 0) {
delete left;
delete right;
delete count;
}
};
class BinStrTree{
private:
TreeNote *root;
public:
BinStrTree():root(new TreeNote()){}
BinStrTree(BinStrTree& r):root(r.root){}
BinStrTree& operator=(const BinStrTree &bst){
TreeNote *new_root = new TreeNote(*bst.root);
delete root;
root = new_root;
return *this;
};
~BinStrTree() { delete root; }
};
其中没有嵌套,所以不会导致递归循环。
class HasPtr {
public:
friend void swap(HasPtr&, HasPtr&);
HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
HasPtr& operator=(const HasPtr &hp) {
auto new_p = new std::string(*hp.ps);
delete ps;
ps = new_p;
i = hp.i;
return *this;
}
~HasPtr() {
delete ps;
}
void show() { std::cout << *ps << std::endl; }
private:
std::string *ps;
int i;
};
void swap(HasPtr& hp1,HasPtr& hp2){
using std::swap;
swap(hp1.ps, hp2.ps);
swap(hp1.i,hp2.i);
cout<<"going swap!"<<endl;
}
bool operator<(HasPtr& hp1, HasPtr& hp2){
return *hp1.ps<*hp2.ps;
}
@Mooophy:
Essentially, the specific avoiding memory allocation is the reason why it improve performance. As for the pointerlike version, no dynamic memory allocation anyway. Thus, a specific version for it will not improve the performance.不用内存分配,所以不会有提升。
首先不能是穿值,因为要对一个Folder对象的值进行更改,传值的话,更改不了。也不能是const因为要对Folder对象的值进行修改。
class Message{
friend class Folder;
public:
explicit Message(const string &str = ""):contents(str){}
Message(const Message& );
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
private:
string contents;
set<Folder*> folders;
void add_to_Folders(const Message&);
void remove_to_Folders();
};
Message::Message(const Message &m):contents(m.contents),folders(m.folders) {add_to_Folders(m);}
Message& Message::operator=(const Message &m) {
remove_to_Folders();
contents = m.contents;
folders = m.folders;
add_to_Folders(m);
}
Message::~Message() { remove_to_Folders();}
void Message::save(Folder &f) {
folders.insert(&f);
f.addMsg(this);
}
void Message::remove(Folder &f) {
folders.erase(&f);
f.remMsg(this);
}
void Message::add_to_Folders(const Message &m) {
for (auto f: m.folders) {
f->addMsg(this);
}
}
void Message::remove_to_Folders() {
for (auto f:folders) {
f->remMsg(this);
}
}
发生拷贝操作时,folder不能进行相应的更新。
class Folder{
public:
void addMsg(Message *m){
messages.insert(m);
}
void remMsg(Message *m){
messages.erase(m);
}
private:
set<Message*> messages;
};
void addfolders(Folder* f){
folders.insert(f);
}
void remfolders(Folder* f){
folders.erase(f);
}
我觉得课能使拷贝交换的形式可能浪费内存。
@Mooophy The copy and swap is an elegant way when working with dynamicly allocated memory. In the Message class , nothing is allocated dynamically. Thus using this idiom makes no sense and will make it more complicated to implement due to the pointers that point back.
@pezy In this case, swap
function is special. It will be clear two Message
's folders , then swap members, and added themselves to each folders. But, Message
assignment operator just clear itself, and copy the members, and added itself to each folders. The rhs
don't need to clear and add to folders. So, if using copy and swap to define, it will be very inefficiency.
class StrVec{
public:
StrVec():elements(nullptr),first_free(nullptr),cap(nullptr){}
StrVec(const StrVec&);
StrVec &operator=(const StrVec&);
~StrVec();
void push_back(const string&);
size_t size() const{ return first_free - elements;}
size_t capaticy() const{ return first_free - cap;}
void reserve(size_t n);
void resize(size_t n);
string *begin() const{return elements;}
string *end() const{return first_free;}
private:
static allocator<string> alloc;//分配内存使用。
void chn_n_alloc(){
if (size() == capaticy()) reallocate();
}
pair<string*, string*>alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string *elements; //指向数组第一个元素的指针
string *first_free;//指向数组第一个空闲元素的指针
string *cap; //指向数组尾后位置的指针
};
void StrVec::reserve(size_t n) {
if (!size()) {
elements = alloc.allocate(n);
first_free = elements;
cap = elements + n;
}
else{
auto newdata = alloc.allocate(n);
auto dest = newdata;
auto elem = elements;
for (auto i = 0 ; i != size(); ++i) {
alloc.construct(dest++,std::move(*elem++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + n;
}
}
void StrVec::push_back(const string &s ) {
chn_n_alloc();
alloc.construct(elements++,s);
}
pair<string*, string*> StrVec::alloc_n_copy(const string *b, const string *e) {//这个不是很懂。
auto data = alloc.allocate(e - b);
return {data, uninitialized_copy(b,e,data)};
}
void StrVec::free() {
if (elements){
for (auto p = first_free;p != elements;) {
alloc.destroy(--p);
}
alloc.deallocate(elements,cap - elements);
}
}
StrVec::StrVec(const StrVec &s) {
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec() {free();}
StrVec &StrVec::operator=(const StrVec &rhs) {
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::resize(size_t n){
if (size() > n){
for(auto p = first_free; p != elements + n;)
alloc.destroy(--p);
first_free = elements + n;
} else
{
if (n >capaticy()) { reserve(n);}
for (auto p = first_free; p != elements + n ; ++p) {
alloc.construct(p, nullptr);
}
first_free = elements + n;
}
}
void StrVec::reallocate() {
auto newcapacity = size()?size()*2:1;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (auto i = 0 ; i != size(); ++i) {
alloc.construct(dest++,std::move(*elem++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
练习13.40 为你的StrVec类添加一个构造函数,它接受一个initializer_list<string>参数。
class StrVec{
public:
StrVec():elements(nullptr),first_free(nullptr),cap(nullptr){}
StrVec(initializer_list<string>&);
StrVec(const StrVec&);
StrVec &operator=(const StrVec&);
~StrVec();
void push_back(const string&);
size_t size() const{ return first_free - elements;}
size_t capaticy() const{ return first_free - cap;}
void reserve(size_t n);
void resize(size_t n);
string *begin() const{return elements;}
string *end() const{return first_free;}
private:
static allocator<string> alloc;//分配内存使用。
void chn_n_alloc(){
if (size() == capaticy()) reallocate();
}
pair<string*, string*>alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string *elements; //指向数组第一个元素的指针
string *first_free;//指向数组第一个空闲元素的指针
string *cap; //指向数组尾后位置的指针
};
StrVec::StrVec(initializer_list<string>& sl){
reserve(sl.size());
auto p = elements;
for (auto it = sl.begin(); it != sl.end(); ++it) {
alloc.construct(p++,*it);
}
first_free = elements + sl.size();
}
void StrVec::reserve(size_t n) {
if (!size()) {
elements = alloc.allocate(n);
first_free = elements;
cap = elements + n;
}
else{
auto newdata = alloc.allocate(n);
auto dest = newdata;
auto elem = elements;
for (auto i = 0 ; i != size(); ++i) {
alloc.construct(dest++,std::move(*elem++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + n;
}
}
void StrVec::push_back(const string &s ) {
chn_n_alloc();
alloc.construct(elements++,s);
}
pair<string*, string*> StrVec::alloc_n_copy(const string *b, const string *e) {//这个不是很懂。
auto data = alloc.allocate(e - b);
return {data, uninitialized_copy(b,e,data)};
}
void StrVec::free() {
if (elements){
for (auto p = first_free;p != elements;) {
alloc.destroy(--p);
}
alloc.deallocate(elements,cap - elements);
}
}
StrVec::StrVec(const StrVec &s) {
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec() {free();}
StrVec &StrVec::operator=(const StrVec &rhs) {
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::resize(size_t n){
if (size() > n){
for(auto p = first_free; p != elements + n;)
alloc.destroy(--p);
first_free = elements + n;
} else
{
if (n >capaticy()) { reserve(n);}
for (auto p = first_free; p != elements + n ; ++p) {
alloc.construct(p, nullptr);
}
first_free = elements + n;
}
}
void StrVec::reallocate() {
auto newcapacity = size()?size()*2:1;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (auto i = 0 ; i != size(); ++i) {
alloc.construct(dest++,std::move(*elem++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
会产生一个错位。first_free的位置还会存东西,而element指向的内存却为空了。
for_each(elements, first_free, [this](std::string &rhs){ alloc.destroy(&rhs); });
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function fn)
{
while (first!=last) {
fn (*first);
++first;
}
return fn; // or, since C++11: return move(fn);
}
首先这个地方for_each是一个模板,实现如上,输入两个迭代器,在这个范围的迭代器执行fn函数,for_each更加的整洁。我倾向于for_each。
空一下。
左值持久;右值短暂。左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
int f();
vector<int> vi(100);
int ? r1= f();//右值引用
int ? r2 = vi[0];//左值引用
int ? r3 = r1;//左值引用
int ? r4 - vi[0]*f();//右值引用
void push_back(std::string &&sb){data->push_back(std::move(sb));}
Foo Foo::sorted() const &{
Foo ret(*this);
return ret.sorted();
}
会进入嵌套循环。跑不出来
Foo Foo::sorted() const &{
return Foo(*this).sorted();
}
会调用右值版本的sorted