13.1
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
1.使用=号
2.将一个对象作为实参传递给一个非引用类型的形参
3.从一个返回类型为非引用类型的函数返回一个对象
4.从一个花括号初始化一个数组中的元素或一个聚合类中的元素
13.2
拷贝构造函数被用来初始化非引用类型参数,自己的参数即形参必须是引用类型。否则为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。
12.3
当我们拷贝StrBlob时, shared_ptr 的引用计数加1。
当我们拷贝 StrBlobPtr 时,引用计数不会变化。
12.4
#include<iostream>
static int cnt = 0;
class Point
{
public:
Point(){}
Point(const Point&p)
{
++cnt;
std::cout << "拷贝构造函数已经调用:" << cnt << "次" << std::endl;
}
};
Point Global;
Point foo_bar(Point arg)// 1
{
Point local = arg, *heap = new Point(Global);//2,3
*heap = local;
Point pa[4] = { local,*heap };//4,5
return *heap;//6
}int main()
{
Point arg;
foo_bar(arg);
return 0;
}
13.5
#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
HasPtr(const HasPtr&);
private:
std::string *ps;
int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
ps = new std::string(*has.ps);
i = has.i;
}
13.6
拷贝复制运算符接受一个与其所在类相同类型的参数,同时为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。
对类对象进行赋值操作时,会使用拷贝赋值运算符。
如果一个类未定义自己的拷贝复制运算符,编译器会为他生成一个合成拷贝赋值运算符。
13.7
前者赋值计数会加一,后者赋值计数不会增加
13.8
#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
HasPtr(const HasPtr&);
//重载赋值运算符
HasPtr& operator=(const HasPtr&);
private:
std::string *ps;
int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
ps = new std::string(*has.ps);
i = has.i;
}
HasPtr& HasPtr::operator=(const HasPtr&has)
{
std::string* temp = new std::string(*has.ps);
delete ps;
ps = has.ps;
i = has.i;
return *this;
}
13.9
析构函数释放对象使用的资源,并销毁对象的非static数据成员。
在对象的最后一次使用之后,进行收尾工作。
当一个类未定义自己的析构函数时。
13.10
前者会递减其引用计数。
后者被销毁时引用计数没有影响。
13.11
~HasPtr() {
if(!ps){
delete ps;
ps = nullptr;}
}
13.12
3次。
accum,iem1,item2.
函数的参数 accum 是按值传递的,如果是值传递,参数在函数调用时会被复制一份,函数结束时,这个副本也会被销毁。因此,即使accum在外部定义,在函数调用时作为参数传递给了fcn函数,也会在函数结束时被销毁。
13.13
X.h
#pragma once
#include<iostream>
class X
{
public:
X() { std::cout << "构造函数X()" << std::endl; }
X(const X&) { std::cout << "拷贝构造函数X(const X&)" << std::endl; }
X& operator=(const X &rhs)
{
std::cout << "拷贝赋值运算符:" << std::endl;
return *this;
}
~X() { std::cout << "析构函数:" << std::endl; }
};
main.cpp
#include"X.h"
int main()
{
X x;
X x2(x);
X x3;
x3 = x;
return 0;
}
13.14
三个相同的数字。
13.15
会改变,在调用f函数时拷贝构造函数会赋予一个新的序号。输出结果时三个不一样的数组。
13.16
会改变,会输出a,b,c原来的值.
13.17
#include<iostream>
class numbered
{
public:
numbered() { mysn = unique++; }
numbered(numbered&) { mysn = ++unique; }
int mysn;
static int unique;
};
int numbered::unique = 0;
void f(numbered s)
{
std::cout << s.mysn << std::endl;
}
int main()
{
numbered a, b = a, c = b;
f(a), f(b), f(c);
return 0;
}
13.18
#pragma once
#include<string>
class Employee
{
public:
Employee(){}
Employee(std::string name_) :name(name_) { id = count++; }
Employee(const Employee& n)
{
this->name = n.name;
id = ++count;
}
private:
std::string name;
int id;
int static count;
};
int Employee::count = 10000;
13.19
需要,否则会造成浅拷贝的问题,即多个对象指向同一片内存空间,在对象销毁时导致多次山粗
13.20
#pragma once
#include<string>
class Employee
{
public:
Employee(){}
Employee(std::string name_) :name(name_) { id = count++; }
Employee(const Employee& n)
{
this->name = n.name;
id = ++count;
}
Employee& operator=(const Employee& n)
{
this->name = n.name;
this->id = ++count;
return *this;
}
private:
std::string name;
int id;
int static count;
};
int Employee::count = 10000;
13.20
编译器会给他们定义合成版本,拷贝会使计数器加一,销毁会减一。
13.21
不需要,因为只能指针会控制他们的销毁。
13.22
#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
HasPtr(const HasPtr&);
~HasPtr() {
if (!ps) {
delete ps;
ps = nullptr;
}
}
//重载赋值运算符
HasPtr& operator=(const HasPtr&);
private:
std::string *ps;
int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
ps = new std::string(*has.ps);
i = has.i;
}
HasPtr& HasPtr::operator=(const HasPtr&has)
{
std::string* temp = new std::string(*has.ps);
delete ps;
ps = has.ps;
i = has.i;
return *this;
}
13.24
未定义析构函数,会发生内存泄漏的问题,编译器的合成析构函数无法删除智能指针。
未定义拷贝构造函数,会发生浅拷贝的问题,合成拷贝构造函数会使两个指针指向同一片内存空间,删除的时候会删除两次。
13.25
需要跟13.22一样的操作,先拷贝,再删除,在赋值,不需要析构是因为只能指针有自己的计数功能。
13.26
#pragma once
#include <string>
#include <initializer_list>
#include <memory>
#include <vector>
#include <stdexcept>
class StrBlobPtr;
class StrBlob
{
public:
friend class StrBlobPtr;
typedef std::vector<std::string>::size_type size_type;
StrBlob();
StrBlob(std::initializer_list<std::string> il);
StrBlob(const StrBlob&);
StrBlob& operator=(const StrBlob&);
size_type size() const { 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;
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const;
};
StrBlob::StrBlob(const StrBlob&rhs)
{
data = std::make_shared<std::vector<std::string>>(*rhs.data);
}
StrBlob& StrBlob::operator=(const StrBlob&rhs)
{
data = std::make_shared<std::vector<std::string>>(*rhs.data);
return *this;
}
class StrBlobPtr
{
public:
StrBlobPtr() : curr(0) {};
StrBlobPtr(const StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
std::string& deref() const;
StrBlobPtr& incr();
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t, const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string &msg) const
{
auto ret = wptr.lock();
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret;
}
std::string& StrBlobPtr::deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlob::StrBlob() : data(std::make_shared<std::vector<std::string>>()) {}
StrBlob::StrBlob(std::initializer_list<std::string> il) : data(std::make_shared<std::vector<std::string>>(il)) {}
void StrBlob::check(size_type i, const std::string &msg) const
{
if (i >= data->size())
throw std::out_of_range(msg);
}
std::string & StrBlob::front()
{
check(0, "front on empty StrBlob");
return data->front();
}
std::string & StrBlob::back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const std::string& StrBlob::front() const
{
check(0, "front on empty StrBlob");
return data->front();
}
const std::string& StrBlob::back() const
{
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
StrBlobPtr StrBlob::begin()const { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end()const
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
13.27
#pragma once
#include<string>
#include<memory>
#include<Cstddef>
class HasPtr
{
public:
// 构造函数分配新的string和新的计数器,将计数器置为1
HasPtr(const std::string&s = std::string()):
ps(new std::string(s)),i(0),use(new std::size_t(1)){}
//拷贝构造函数拷贝所有的三个数据成员,并且递增计数器
HasPtr(const HasPtr&p):
ps(p.ps), i(p.i), use(p.use) {
++*use;
}
HasPtr& operator=(const HasPtr&);
//析构函数,计数器为0,清空
~HasPtr()
{
if (--*use == 0)
{
delete ps;// 释放string内存
delete use; // 释放计数器内存
}
}
private:
std::string *ps;
int i;
size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
++*rhs.use;
if (--*use == 0)
{
delete ps;
delete use;
}
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this;
}
13.28
tree.h
#pragma once
#include<string>
using std::string;
class TreeNode
{
public:
TreeNode():
value(string()),count(new int(1)),left(nullptr),right(nullptr){}
TreeNode(const TreeNode&rhs) :
value(rhs.value), count(rhs.count), left(rhs.left), right(rhs.right){++*count;}
TreeNode& operator=(TreeNode&);
~TreeNode()
{
if (*--count == 0)
{
delete left;
delete right;
delete count;
}
}
private:
string value;
int *count;
TreeNode *left;
TreeNode *right;
};
class BinStrTree
{
public:
BinStrTree():root(new TreeNode()){}
BinStrTree(const BinStrTree&bst) :root(new TreeNode(*bst.root)){}
BinStrTree& operator=(const BinStrTree&);
~BinStrTree()
{
delete root;
}
private:
TreeNode *root;
};
main.cpp
#include"Tree.h"
TreeNode& TreeNode::operator=(TreeNode&rhs)
{
++*rhs.count;
if (*--count == 0)
{
delete left;
delete right;
delete count;
}
value = rhs.value;
left = rhs.left;
right = rhs.right;
count = rhs.count;
return *this;
}
BinStrTree& BinStrTree::operator=(const BinStrTree&bst)
{
TreeNode *new_root = new TreeNode(*bst.root);
delete root;
root = new_root;
return *this;
}
int main()
{
return 0;
}
13.29
有三个版本的swap
swap(HasPtr&lhs, HasPtr&rhs)是我们定义的;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);是std自带的。
13.30
#pragma once
#include<string>
#include<memory>
#include<iostream>
#include<Cstddef>
class HasPtr
{
friend void swap(HasPtr&, HasPtr&);
public:
// 构造函数分配新的string和新的计数器,将计数器置为1
HasPtr(const std::string&s = std::string()):
ps(new std::string(s)),i(0),use(new std::size_t(1)){}
//拷贝构造函数拷贝所有的三个数据成员,并且递增计数器
HasPtr(const HasPtr&p):
ps(p.ps), i(p.i), use(p.use) {
++*use;
}
HasPtr& operator=(const HasPtr&);
//析构函数,计数器为0,清空
~HasPtr()
{
if (--*use == 0)
{
delete ps;// 释放string内存
delete use; // 释放计数器内存
}
}
private:
std::string *ps;
int i;
size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
++*rhs.use;
if (--*use == 0)
{
delete ps;
delete use;
}
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this;
}
inline void swap(HasPtr&lhs, HasPtr&rhs)
{
using std::swap;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);
std::cout << "swap函数调用" << std::endl;
}
13.31
hasptr.h
#pragma once
#include<string>
#include<memory>
#include<iostream>
#include<Cstddef>
#include<vector>
class HasPtr
{
friend void swap(HasPtr&, HasPtr&);
friend bool operator<(const HasPtr&lhs, const HasPtr&rhs);
public:
// 构造函数分配新的string和新的计数器,将计数器置为1
HasPtr(const std::string&s = std::string()):
ps(new std::string(s)),i(0),use(new std::size_t(1)){}
//拷贝构造函数拷贝所有的三个数据成员,并且递增计数器
HasPtr(const HasPtr&p):
ps(p.ps), i(p.i), use(p.use) {
++*use;
}
HasPtr& operator=(const HasPtr&);
//析构函数,计数器为0,清空
~HasPtr()
{
if (--*use == 0)
{
delete ps;// 释放string内存
delete use; // 释放计数器内存
}
}
void show()const
{
std::cout << *ps << std::endl;
}
private:
std::string *ps;
int i;
size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
++*rhs.use;
if (--*use == 0)
{
delete ps;
delete use;
}
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this;
}
bool operator<(const HasPtr&lhs, const HasPtr&rhs)
{
return *lhs.ps < *rhs.ps;
}
inline void swap(HasPtr&lhs, HasPtr&rhs)
{
using std::swap;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);
std::cout << "swap函数调用" << std::endl;
}
main.cpp
#include"HasPtr.h"
#include<vector>
#include<algorithm>
int main()
{
HasPtr h1("aaa");
HasPtr h2("bbb");
HasPtr h3("ccc");
std::vector<HasPtr> v1{ h1,h3,h2 };
std::sort(v1.begin(), v1.end());
for (auto &i : v1)
i.show();
return 0;
}
13.32
类值版本的swap通过指针交换内存地址,不用开辟新的内存空间从而受益,
类指针版本可以直接交换地址,所以不会收益。
13.33
因为save和Move需要向folder中插入或删除某个元素。
13.34 &
36
#pragma once
#include<string>
#include<set>
class Folder;
class Message
{
friend class Folder;
public:
explicit Message(const std::string &str = " "):
contents(str){}
//考本控制成员,用来管理本message的指指针
Message(const Message&); //拷贝构造函数
Message& operator=(Message&);//拷贝赋值运算符
~Message();//析构函数
//从给定folder中添加、删除本message
void save(Folder&);
void remove(Folder&);
// 通过定义swap,避免对contents和folders成员进行不必要的拷贝
void swap(Message &lhs, Message &rhs);
private:
std::string contents; //包含实际文本
std::set<Folder*> folders; //包含本message的folder
//构造函数使用的工具函数
//将本message添加到对应参数的folder中
void add_to_Folder(const Message&);
//从folder中的每个folder中删除本message
void remove_form_Folders();
};
class Folder
{
public:
void addMsg(Message*); //将本message添加到message的集合中
void remMsg(Message*); //将本message从message的集合中取出
private:
std::set<Message*>mgs;
};
void Message::save(Folder &f)
{
folders.insert(&f); //将给定Folder添加到我们的F列表中
f.addMsg(this);//将本message添加到message的集合中
}
void Message::remove(Folder &f)
{
folders.erase(&f); //将给定Folder添加到我们的F列表中
f.remMsg(this); //将本message从message的集合中取出
}
//将本Message添加到指向M的Folder中
void Message::add_to_Folder(const Message&m)
{
for (auto f : m.folders) //对每个包含m的Folder
f->addMsg(this); //向该Folder添加一个指向本Message的指针
}
//拷贝构造函数 拷贝给定对象的数据成员
Message::Message(const Message& m):contents(m.contents),folders(m.folders)
{
add_to_Folder(m); //将本消息添加到指向m的Folder中
}
//从对应的Folder中删除本message
void Message::remove_form_Folders()
{
for (auto f : folders)
f->remMsg(this);
}
//析构函数
Message::~Message()
{
remove_form_Folders();
}
//拷贝赋值运算符
Message& Message::operator=(Message&rhs)
{
//先删除指针
remove_form_Folders();//更新已有Folder
contents = rhs.contents; //拷贝实际内容
folders = rhs.folders; //从rhs拷贝Folder指针
add_to_Folder(rhs); //将本message添加到f中
return *this;
}
void Message::swap(Message &lhs, Message &rhs)
{
using std::swap;
//将每个消息的指针从它原来所在的Floders中删除
for (auto f : lhs.folders)
f->remMsg(&lhs);
for (auto f : rhs.folders)
f->remMsg(&rhs);
swap(lhs.folders, rhs.folders);
swap(lhs.contents, rhs.contents);
// 将每个消息的指针添加到新的Floders中
for (auto f : lhs.folders)
f->addMsg(&lhs);
for (auto f : rhs.folders)
f->addMsg(&rhs);
}
void Folder::addMsg(Message* m) //将本message添加到message的集合中
{
mgs.insert(m);
}
void Folder::remMsg(Message* m) //将本message从message的集合中取出
{
mgs.erase(m);
}
13.35
在赋值以后Message和Folder将会不同步
13.37
#pragma once
#include<string>
#include<set>
class Folder;
class Message
{
friend class Folder;
public:
explicit Message(const std::string &str = " "):
contents(str){}
//考本控制成员,用来管理本message的指指针
Message(const Message&); //拷贝构造函数
Message& operator=(Message&);//拷贝赋值运算符
~Message();//析构函数
//从给定folder中添加、删除本message
void save(Folder&);
void remove(Folder&);
// 通过定义swap,避免对contents和folders成员进行不必要的拷贝
void swap(Message &lhs, Message &rhs);
private:
std::string contents; //包含实际文本
std::set<Folder*> folders; //包含本message的folder
//构造函数使用的工具函数
//将本message添加到对应参数的folder中
void add_to_Folder(const Message&);
//从folder中的每个folder中删除本message
void remove_form_Folders();
//向folder中添加或删除一个给定的Folder*
void addFldr(Folder *f) { folders.insert(f); }
void remFldr(Folder *f) { folders.erase(f); }
};
class Folder
{
friend class Message;
public:
Folder() = default;
Folder(const Folder&);
Folder& operator=(const Folder&);
~Folder();
void addMsg(Message*); //将本message添加到message的集合中
void remMsg(Message*); //将本message从message的集合中取出
private:
std::set<Message*>mgs;
//向添加的Folder*里面加入message
void add_to_message(const Folder&);
void remove_from_message();
};
void Message::save(Folder &f)
{
folders.insert(&f); //将给定Folder添加到我们的F列表中
f.addMsg(this);//将本message添加到message的集合中
}
void Message::remove(Folder &f)
{
folders.erase(&f); //将给定Folder添加到我们的F列表中
f.remMsg(this); //将本message从message的集合中取出
}
//将本Message添加到指向M的Folder中
void Message::add_to_Folder(const Message&m)
{
for (auto f : m.folders) //对每个包含m的Folder
f->addMsg(this); //向该Folder添加一个指向本Message的指针
}
//拷贝构造函数 拷贝给定对象的数据成员
Message::Message(const Message& m):contents(m.contents),folders(m.folders)
{
add_to_Folder(m); //将本消息添加到指向m的Folder中
}
//从对应的Folder中删除本message
void Message::remove_form_Folders()
{
for (auto f : folders)
f->remMsg(this);
}
//析构函数
Message::~Message()
{
remove_form_Folders();
}
//拷贝赋值运算符
Message& Message::operator=(Message&rhs)
{
//先删除指针
remove_form_Folders();//更新已有Folder
contents = rhs.contents; //拷贝实际内容
folders = rhs.folders; //从rhs拷贝Folder指针
add_to_Folder(rhs); //将本message添加到f中
return *this;
}
void Message::swap(Message &lhs, Message &rhs)
{
using std::swap;
//将每个消息的指针从它原来所在的Floders中删除
for (auto f : lhs.folders)
f->remMsg(&lhs);
for (auto f : rhs.folders)
f->remMsg(&rhs);
swap(lhs.folders, rhs.folders);
swap(lhs.contents, rhs.contents);
// 将每个消息的指针添加到新的Floders中
for (auto f : lhs.folders)
f->addMsg(&lhs);
for (auto f : rhs.folders)
f->addMsg(&rhs);
}
void Folder::addMsg(Message* m) //将本message添加到message的集合中
{
mgs.insert(m);
}
void Folder::remMsg(Message* m) //将本message从message的集合中取出
{
mgs.erase(m);
}
void Folder::add_to_message(const Folder& f)
{
for (auto m : f.mgs)
m->addFldr(this);
}
void Folder::remove_from_message()
{
for (auto m : mgs)
m->remFldr(this);
}
Folder::~Folder()
{
remove_from_message();
}
Folder& Folder::operator=(const Folder& rhs)
{
remove_from_message();
mgs = rhs.mgs;
add_to_message(rhs);
return *this;
}
13.38
swap主要是用于动态内存时,这里并没有使用动态内存。
13.39
头文件
#pragma once
#include<string>
#include<memory>
/*实现vector的简化版本
用于分配动态内存*/
class StrVec
{
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
//老三样
StrVec(const StrVec&);
StrVec& operator=(const StrVec&);
~StrVec();
void push_back(const std::string&);//拷贝元素
size_t size()const { return first_free - elements; } //实际大小
size_t capacity() const { return cap - first_free; }//容量
std::string *begin() const { return elements; }
std::string *end() const { return first_free; }//相当于get函数
void reserve(size_t new_cap);
void resize(size_t count);
void resize(size_t count, const std::string&);
private:
std::allocator<std::string> alloc;// 分配元素
// 被添加元素的函数所使用
void chk_n_alloc() { if (size() == capacity()) reallocate();}
// 工具函数,被三拷贝函数使用
std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);
void free(); //销毁元素并释放内存
void reallocate(); //获得更多内存并拷贝已有元素
void alloc_n_move(size_t new_cap);
//三个模仿vector迭代器的指针
std::string *elements; //指向数组首元素的指针
std::string *first_free; //指向实际元素之后的第一个位置
std::string *cap;// 指向数组尾后的指针
};
源文件
#include"StrVec.h"
void StrVec::push_back(const std::string &s)
{
chk_n_alloc(); //check一下是否内存已满
alloc.construct(first_free++, s); //使用后置++
}
std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string*b, const std::string*e)
{
//分配空间保存给定范围中的元素
auto data = alloc.allocate(e - b);
// 初始化返回一个pair,该pair由data和copy的返回值构成
// 返回的pair的first成员指向首地址,second成员返回的是尾后的位置
return { data,std::uninitialized_copy(b,e,data) };
}
void StrVec::free()
{
// 不能传递给deallocata一个空指针
if (elements)
{
//逆序销毁旧元素
for (auto p = first_free; p != elements;)
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
}
StrVec::StrVec(const StrVec&s)
{
//调用alloc_n_copy
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)
{
/*newdata 本身不是左侧运算符自身内存的一部分。
newdata.first 和 newdata.second 存储的是动态内存的指针,
这些指针指向的内存块是通过 allocator 对象的 allocate 成员函数从堆中动态分配的,不是位于 StrVec 对象内部的内存块。
因此,在 free 函数中销毁元素和释放内存时,newdata 不会受到影响,也不会被释放掉。*/
auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = newdata.first;
first_free = cap = newdata.second;
return *this;
}
void StrVec::reallocate()
{
//分配当前大小两倍的内存空间
auto newcapacity = size() ? 2 * size() : 1;
//分配新内存
auto newdata = alloc.allocate(newcapacity);
// 将数据从就内存转移到新内存
auto dest = newdata; //指向新数组中下一个空闲位置
auto elem = elements;//就数组中下一个元素
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
void StrVec::alloc_n_move(size_t new_cap)
{
auto newdata = alloc.allocate(new_cap);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + new_cap;
}
void StrVec::reserve(size_t newcap)
{
if (newcap <= capacity()) return;
alloc_n_move(newcap);
}
void StrVec::resize(size_t newcap)
{
resize(newcap, std::string());
}
void StrVec::resize(size_t newcap, const std::string &s)
{
if (newcap >= size())
{
if (newcap >= capacity())reserve(newcap * 2);
for (size_t i = size(); i != newcap; ++i)
alloc.construct(first_free++, s);
}
else if (newcap < size())
{
while (first_free != elements + newcap)
alloc.destroy(--first_free);
}
}
int main()
{
return 0;
}
13.40
StrVec::StrVec(std::initializer_list<std::string> lis)
{
auto newdata = alloc_n_copy(lis.begin(), lis.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
13.41
因为顺序是先construct,后++,所以使用后置的++运算符。
如果使用前置的话,会导致first_free指向的第一个元素为未创建的。
13.42
#pragma once
#include<fstream>
#include<iostream>
#include<sstream>
//#include<vector>
#include<memory>
#include<string>
#include<map>
#include<set>
#include<algorithm>
#include"StrVec.h"
class QueryResult;
class TextQuery
{
public:
using size_type = size_t;
TextQuery(std::ifstream&);
QueryResult query(const std::string&)const;
private:
std::shared_ptr<StrVec> file; // 输入文件
std::map<std::string, std::shared_ptr<std::set<size_type>>> wm; //每个单词与行号的map
};
class QueryResult
{
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(std::string s, std::shared_ptr<std::set<TextQuery::size_type>> p,
std::shared_ptr<StrVec> f) : sought(s), lines(p), file(f) { }
private:
std::string sought;
std::shared_ptr<std::set<TextQuery::size_type>> lines;
std::shared_ptr<StrVec> file;
};
TextQuery::TextQuery(std::ifstream&ifs) :file(new StrVec)
{
std::string text;
while (getline(ifs, text))
{
file->push_back(text);
int n = file->size() - 1;
std::istringstream line(text);
std::string text;
while (line >> text)
{
std::string word;
std::copy_if(text.begin(), text.end(), std::back_inserter(word), isalpha);
auto &lines = wm[word];
if (!lines)
lines.reset(new std::set<size_type>);
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const std::string &sought) const
{
static std::shared_ptr<std::set<TextQuery::size_type>> nodata(new std::set<TextQuery::size_type>);
auto loc = wm.find(sought);
if (loc == wm.end())
return QueryResult(sought, nodata, file);
else
return QueryResult(sought, loc->second, file);
}
std::ostream &print(std::ostream &os, const QueryResult &qr)
{
os << qr.sought << " occurs " << qr.lines->size() << " " /*<< make_plural(qr.lines->size(), "time", "s")*/ << std::endl;
for (auto num : *qr.lines)
os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << std::endl;
return os;
}
// main函数可能要改一下
int main()
{
return 0;
}
13.43
for_each(first_free, elements, [this](std::string &rhs)
{alloc.destroy(&rhs); });
更倾向于前面的
13.44&47&48
头文件
#pragma once
#include<memory>
class newString
{
public:
newString():elements(nullptr),end(nullptr){}
newString(const char*);
newString(const newString&);
newString& operator=(const newString&);
~newString();
const char *c_str() const { return elements; }
size_t size() { return end - elements; }
size_t length() { return end - elements - 1; }
private:
std::pair<char*, char*> alloc_n_copy(const char*,const char*);
void range_initializer(const char*, const char*);
void free();
char *elements;
char *end;
std::allocator<char> alloc;
};
源文件
#include"newstring.h"
#include<iostream>
#include<algorithm>
#include<vector>
std::pair<char*, char*> newString::alloc_n_copy(const char*beg, const char*end)
{
auto str = alloc.allocate(end - beg);
return { str,std::uninitialized_copy(beg,end,str) };
}
void newString::range_initializer(const char*beg, const char*end)
{
auto newstr = alloc_n_copy(beg, end);
elements = newstr.first;
end = newstr.second;
}
newString::newString(const char *s)
{
char *sl = const_cast<char*>(s);
while (*sl)
++sl; //指向尾后
range_initializer(s, ++sl);
}
newString::newString(const newString& rhs)
{
range_initializer(rhs.elements, rhs.end);
std::cout << "copy constructor" << std::endl;
}
void newString::free()
{
if (elements) {
std::for_each(elements, end, [this](char &c) { alloc.destroy(&c); });
alloc.deallocate(elements, end - elements);
}
}
newString::~newString()
{
free();
}
newString& newString::operator = (const newString &rhs)
{
auto newstr = alloc_n_copy(rhs.elements, rhs.end);
free();
elements = newstr.first;
end = newstr.second;
std::cout << "copy-assignment" << std::endl;
return *this;
}
void foo(newString x)
{
std::cout << x.c_str() << std::endl;
}
void bar(const newString& x)
{
std::cout << x.c_str() << std::endl;
}
newString baz()
{
newString ret("world");
return ret;
}
int main()
{
char text[] = "world";
newString s0;
newString s1("hello");
newString s2(s0);
newString s3 = s1;
newString s4(text);
s2 = s1;
foo(s1);
bar(s1);
foo("temporary");
bar("temporary");
newString s5 = baz();
std::vector<newString> svec;
svec.reserve(8);
svec.push_back(s0);
svec.push_back(s1);
svec.push_back(s2);
svec.push_back(s3);
svec.push_back(s4);
svec.push_back(s5);
svec.push_back(baz());
svec.push_back("good job");
for (const auto &s : svec) {
std::cout << s.c_str() << std::endl;
}
}
13.45
对于常规引用,成为左值引用。
绑定到右值的引用成为右值引用。
13.46
int &&r1=f();
int &r2=vi[0];
int &r3=r1;
int &&r4 = vi[0]*f();
13.49
StrVec:
//移动构造函数
StrVec::StrVec(StrVec&&s) noexcept:
elements(s.elements),first_free(s.first_free),cap(s.cap)
{
//接管以后令右值指针都置空
s.elements = s.first_free = s.cap = nullptr;
}
// 移动赋值运算符
StrVec& StrVec::operator=(StrVec&&rhs) noexcept
{
//直接检测自赋值
if (this != &rhs)
{
free(); //释放this
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
string:
//移动构造函数
newString::newString(newString&&s)noexcept :elements(s.elements),end(s.end)
{
s.elements = s.end = nullptr;
}
newString& newString::operator=(newString&&rhs)noexcept
{
if (this != &rhs)
{
free();
elements = rhs.elements;
end = rhs.end;
rhs.elements = rhs.end = nullptr;
}
return *this;
}
Message
//移动构造函数
Message::Message(Message &&m) :contents(std::move(m.contents))
{
move_Folders(&m);
}
//移动赋值运算符
Message& Message::operator=(Message&&rhs)
{
if (this != &rhs) {
remove_form_Folders();
contents = std::move(rhs.contents);
move_Folders(&rhs);
}
return *this;
}
13.50
String baz()
{
String ret("world");
return ret; // 1
}
String s5 = baz(); // 2
13.51
在这里是移动的操作而不是拷贝
13.52
左值被拷贝,右值被移动
13.53
不会
13.54
/*error: ambiguous overload for 'operator=' (operand types are 'HasPtr' and 'std::remove_reference<HasPtr&>::type { aka HasPtr }')
hp1 = std::move(*pH);
^*/
13.55
void push_back(string &&s) { data->push_back(std::move(s)); }
13.56
会产生递归造成溢出
13.57
没有问题,会使用移动版本的
13.58
#include <vector>
#include <iostream>
#include <algorithm>
class Foo {
public:
Foo sorted() &&;
Foo sorted() const &;
private:
vector<int> data;
};
Foo Foo::sorted() && {
sort(data.begin(), data.end());
std::cout << "&&" << std::endl;
return *this;
}
Foo Foo::sorted() const & {
std::cout << "&" << std::endl;
return Foo(*this).sorted();
}
int main()
{
Foo().sorted();
Foo f;
f.sorted();
}