普通构造函数:
普通构造函数就没什么好说的了
拷贝构造函数:
当我们需要用一个对象初始化另一个对象时,我们就需要拷贝初始化
在形式上长这样fuck(const fuck& a){}
const不是必须的但是通常情况下是const的
那么什么时候会用到拷贝构造呢,普遍意义下,只用我们需要用一个对象初始化另一个对象时,那么我们就要使用了
比如
fuck a = fuck(5) fuck a = c;
值传递时,返回非引用对象时等等
但是有一个问题,就是编译器可能会绕过拷贝构造,这个时候不会显式的调用拷贝构造,不同的编译器处理方式不同
但是这些绕过的操作也需要拷贝构造的存在,比如我们将拷贝构造声明为private,explicit,都会报错
拷贝赋值:
拷贝赋值和拷贝构造差异不大,主要需要注意深拷贝,浅拷贝,自赋值的问题
深拷贝即先备份再赋值,然后释放旧值
浅拷贝直接赋值,释放旧值
自赋值注意下
移动构造:
当我们从一个即将不需要的对象初始化另一个对象时,如果使用拷贝初始化时,会显得太傻
那么移动构造应运而生,移动构造即把一个右值引用对象移动到新的对象中,这个过程没用出现
多余的拷贝操作问题
形式为fuck(const &&a);参数为一个右值引用
库函数std::move可以将一个左值变为右值
移动赋值:
同样注意子赋值的问题
注意:我们一定要保证右值对象移动后,保证其为可析构的,并且是安全的
右值左值:
这两者在c++中的区别比在c中的区别抽象了点,主要时左值用到了对象的身份,而右值用到了值本身或这是一个临时对象
c++中允许向一个右值赋值,我们可以用引用限定符来限定这种操作
析构函数:
下面几种情况会使用析沟函数:
变量离开作用域
对象被销毁时,其成员也被销毁
容器被销毁时,其中的元素会销毁
delete动态分配内存时
…
注意: 销毁对象,释放内存,这些事不是析沟函数函数体做的事,而是析构函数的另一个阶段
差不多这些就是c++ primer13章学习的感受,收益良多,所以写了写自己粗浅的见解
vector的简易实现:
注意非const static成员要在类外定义,否则链接的时候找不到
#include<iostream>
#include<cstdio>
#include<memory>
#include<string>
using namespace std;
class StrVec
{
public:
StrVec():elements(nullptr),cap(nullptr),first_free(nullptr){};
StrVec(initializer_list<string>a):elements(nullptr),cap(nullptr),first_free(nullptr){
auto be = a.begin();
for(int i = 0;i<a.size();i++){
check_n_alloc();
push_back(*be++);
}
}
StrVec(const StrVec &a);
StrVec& operator=(const StrVec &a);
StrVec( StrVec &&a) noexcept;
StrVec& operator=(StrVec &&a) noexcept;
~StrVec();
void push_back(const string& s);
int size() const{
return first_free-elements;
}
int capacity() const{
return cap-elements;
}
string* begin() const
{
return elements;
}
string* end() const
{
return first_free;
}
private:
pair<string*,string*> alloc_n_copy(const string* a,const string *b);
static allocator<string> alloc;
string *elements;
string *cap;
string *first_free;
void free();
void reallocate();
void check_n_alloc()
{
if(size()==capacity()) reallocate();
}
};
allocator<string> StrVec:: alloc;
StrVec::StrVec(const StrVec &a)
{
auto data = alloc_n_copy(a.begin(),a.end());
elements = data.first;
first_free = cap = data.second;
}
StrVec& StrVec::operator=(const StrVec &a){
auto data = alloc_n_copy(a.begin(),a.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
StrVec::StrVec( StrVec &&a) noexcept{
elements = a.elements;
first_free = a.first_free;
cap = a.cap;
a.elements = a.first_free = a.cap = nullptr;
}
StrVec& StrVec::operator=(StrVec &&a) noexcept{
if(this==&a){
}
else{
elements = a.elements;
first_free = a.first_free;
cap = a.cap;
a.elements = a.first_free = a.cap = nullptr;
}
}
StrVec::~StrVec(){
free();
}
void StrVec::push_back(const string& s){
check_n_alloc();
alloc.construct(first_free++,s);
}
pair<string* ,string*> StrVec::alloc_n_copy(const string *a,const string *b)
{
auto data = alloc.allocate(b-a);
return {data,uninitialized_copy(a,b,data)};
}
void StrVec::free()
{
if(elements){
for(string *i = elements;i!=first_free;i++){
alloc.destroy(i);
}
alloc.deallocate(elements,cap-elements);
}
}
void StrVec::reallocate()
{
int new_size = size()?size()*2:1;
auto data = alloc.allocate(new_size);
auto new_elements = data;
auto tmp_elements = elements;
for(int i = 0;i<size();i++){
alloc.construct(new_elements++,move(*tmp_elements++));
}
free();
elements = data;
first_free = new_elements;
cap = data+new_size;
}
int main()
{
StrVec a({"ffsfsf","fsfsf","fffff"});
a.push_back("ffff");
cout<<a.size()<<endl;
}