对象被优化后c++才是高效的

对象使用过程中背后使用哪些方法

class test
{
public:
    test(int a=10) : ma(a){cout<<"test()"<<endl;}
    ~test(){cout<<"~test()"<<endl;}
    test(const test& t) :ma(t.ma){cout<<"const test*"<<endl;}
    test& operator=(const test&t)
    {
        cout<<"operator="<<endl;
        this->ma = t.ma;
        return *this;
    }
private:
    int ma;
};

int main() {
    test t1;
    test t2(t1);
    test t3 = t1;
    //对象的初始化,调用拷贝构造
    cout<<"----------------"<<endl;
    test t4 = test(20);
    //用于临时对象生成的新对象,临时对象生成的时候,
    // 临时对象不产生了,直接构造新对象就可以了
    cout<<"-----------------"<<endl;
    t4 = t2;
    t4 = test(30);
    cout<<"------------------"<<endl;
    t4 = (test)30;
    //int -> test 编译器要寻找有没有=合适的构造函数来完成类型转换
    //若存在就会隐式生成一个临时对象
    //调用operator=
    //临时对象析构
    cout<<"------------------"<<endl;
    t4  = 30;
    //隐式转换 从 int -> test 生成临时对象
    //调用operator=
    //临时对象析构
    cout<<"----------------"<<endl;
    return 0;
}
输出:
test()
const test*
const test*
----------------
test()
-----------------
~test*()
~test*()
~test*()
~test*()


int main()
{
    test* p = &test(40);
    test& ref = test(50);
    //const test& ret = test(50) 可以,常引用用于对于临时对象的引用,这拓展了临时对象的生命周期(与ref相同)
}
编译错误,临时对象在语句结束后消失,在语句执行完之后,p指向的test对象已经析构,ref同理。所以没有意义

c++编译器对于对象构造的优化:用于临时对象生成的新对象,临时对象生成的时候,临时对象不产生了,直接构造新对象就可以了

//注释表明了发生的顺序
class Test
{
public:
   // Test() Test(10) Test(10, 10)
   Test(int a = 5, int b = 5) 
      :ma(a), mb(b)
   {
      cout << "Test(int, int)" << endl;
   }
   ~Test()
   {
      cout << "~Test()" << endl;
   }
   Test(const Test &src) 
      :ma(src.ma), mb(src.mb)
   {
      cout << "Test(const Test&)" << endl;
   }
   void operator=(const Test &src)
   {
      ma = src.ma; 
      mb = src.mb; 
      cout << "operator=" << endl;
   }
private:
   int ma;
   int mb;
};
Test t1(10, 10);                    								// 1.Test(int, int)  
int main()
{
   Test t2(20, 20);                 								// 3.Test(int, int)
   Test t3 = t2;                    								// 4.Test(const Test&)
   // static Test t4(30, 30);
   static Test t4 = Test(30, 30);   					// 5.Test(int, int),虽然这也是全局的但是位于函数内部
   t2 = Test(40, 40); 											   // 6.Test(int, int) operator= ~Test()
  			 													  // (50, 50) =  (Test)50; Test(int)
   t2 = (Test)(50, 50); 											// 7.Test(int,int) operator=  ~Test()
   t2 = 60; //Test(int) 8.Test(int,int) operator= ~Test()
   Test *p1 = new Test(70, 70); 									// 9. Test(int,int) 
   Test *p2 = new Test[2]; 											// 10. Test(int,int) Test(int,int)
   Test *p3 = &Test(80, 80); 										// 11. Test(int,int)  ~Test()
   const Test &p4 = Test(90, 90);									 // 12. Test(int,int)
   delete p1;                                                           // 13.~Test()
   delete[]p2;                                                          // 14. ~Test() ~Test()
}
Test t5(100, 100);													// 2.Test(int, int)  全局变量优先

函数调用中调用了对象的那些方法

#include <iostream>
using namespace std;


class test
{
public:
    test(int a=10) : ma(a){cout<<"test()"<<endl;}
    ~test(){cout<<"~test()"<<endl;}
    test(const test& t) :ma(t.ma){cout<<"const test*"<<endl;}
    test& operator=(const test&t)
    {
        cout<<"operator="<<endl;
        this->ma = t.ma;
        return *this;
    }
    int getdata() const
    {
        return ma;
    }
private:
    int ma;
};
test GetObjct(test t)
//传参是对发生拷贝构造
{
    int val = t.getdata();
    test tem = test(val);
    //调用构造函数生成临时对象,将临时对象operator=赋值给tem
    return tem;
}

int main()
{
    test t1; //调用构造函数
    test t2; //调用构造函数
    cout<<"------------------"<<endl;
    t2 = GetObjct(t1);
    /*
    传参过程 t1 -> t 相当于为t1对象初始化,调用拷贝构造
    test(val) 调用构造函数产生临时对象
    test tem = test(val) 为对象tem初始化调用拷贝构造
    return tem 将tem对象拷贝止main函数的栈帧中生成临时对象调用拷贝构造
    栈帧消失对象依次析构(与构造顺序相反) tem对象析构  t对象析构
    t2 = 返回的临时对象   t2是已经存在,所以这是为t2赋值 调用operator=
    */
    return 0;
}

总结三条对象优化的规则

  1. 函数参数传递过程中,对象优先按引用传递,不要按值传递(避免了参数传递中的拷贝过程与函数结束后被拷贝对象的析构过程)
  2. 当函数返回对象时,要优先返回一个临时对象,而不是返回一个定义过的对象
  3. 接受返回值是对象的函数调用的时候,应该优先按照初始化的方式接受而不是按照赋值的方式接受

返回对象

test GetObjct(test& t)
{
    int val = t.getdata();  
	test tem = test(val);  //将一个临时对象拷贝至一个新对象,先构造在拷贝构造
    return tem;			  //将一个对象拷贝到站外的临时对象 ,发生拷贝构造
}						//函数结束后tem对象发生析构

优化为

test GetObject(test &t)
{
	int val = t.getdata();
	return test(val);
	/*
	返回一个临时对象到函数栈外用于初始化一个新的对象(也是临时对象)
	这句话有没有很熟悉
	c++编译器对于对象构造的优化:用于临时对象生成的新对象,临时对象生成的时候,临时对象不产生了,直接构造新对象就可以了
	所以这只调用了一次构造函数
	*/
}

接受对象:

int main()
{
    test t1;
    test t2;
    cout<<"------------------"<<endl;
    t2 = GetObjct(t1);
    cout<<"----------------"<<endl;
    return 0;
}

改为:

int main()
{
    test t1;
    cout<<"------------------"<<endl;
    test t2 = GetObjct(t1);			
    //GetObjct(ti) 返回一个临时对象之后要拷贝规则到拎一个临时对象中
    //而这个临时对象又要用来初始化t2
    //这就导致了只在函数中执行了一次构造函数就为t2完成了赋值操作
    cout<<"----------------"<<endl;
    return 0;
}

CMystring的代码问题 右值引用

先来看下原来写的String实现的代码:

class Mystring
{
public:
    Mystring(const char* str = nullptr)
    {
        cout<<"Mystring"<<endl;
        if(str == nullptr)
        {
            ptr = new char('0');
        }
        else
        {
            ptr = new char[sizeof(str) + 1];
            strcpy(ptr, str);
        }
    }
    ~Mystring()
    {
        cout<<"~Mystring"<<endl;
        delete[] ptr;
        ptr = nullptr;
    }
    Mystring(const Mystring& another)
    {
        cout<<"Mystring(const Mystring& another)"<<endl;
        ptr = new char[strlen(another.ptr)  + 1];
        strcpy(ptr,another.ptr);
    }
    Mystring& operator=(const Mystring&another)
    {
        if(this == &another)
            //this 和 &another 是地址值可以比较
            //*this 和 another 是Mystring的对象,这两个比较要重载 == 运算符
            return *this;
        delete[] this->ptr;
        this->ptr = new char[strlen(another.ptr) + 1];
        strcpy(ptr,another.ptr);
        return *this;
    }
    const char* c_str()
    {
        return ptr;
    }
private:
    char* ptr;
};

Mystring GetString(Mystring& str)
{
    const char* pstr = str.c_str();
    return Mystring(pstr);        //构造   ->   拷贝
}
int main()
{
    Mystring str1("aaaaaaaaaaaa");   //构造
    Mystring str2;                       //构造
    str2 = GetString(str1);           //operator=
    cout<<str2.c_str()<<endl;            
    return 0;
}

这里的拷贝构造和赋值重载是最为浪费资源的函数,因为这里涉及到了字符串的拷贝,要知道在现实中要传递的字符串是非常长的比如json 格式,拷贝并不是最可悲的,而是我费劲的完成了拷贝之后这个对象就用一次之后就消失了

这样不如直接使用临时对象

右值引用:

左值:有名字并且有内存 右值:没名字或者没内存(存在寄存器中)

int main()
{
    int  a = 10;
    int &c = a;
    //int &&d = a;                  不能用右值值引用绑定左值
    //int & d = 20;                 不能用左值引用绑定右值
    const int&d = 20;               //可以使用常引用绑定右值,但是这个内存的值将不可修改
    /*相当于先生成一个临时量赋值为20 int tem = 20   tem是一个临时对象
     * const int&d = tem;   在对这个临时量进行引用
     * */
    int&& e = 20;
    /*相当于:先生成一个临时量赋值为20 int tem = 20   tem是一个临时对象
    * const int&d = tem;   在对这个临时量进行引用
    */
    //int &&e = 40;  //右值引用同样不可以修改引用的对象
    e = 30;          //右值引用可以修改指向内存的值
    /*int &&f = e;
    报错:'int'类型的右值引用不能绑定到'int'类型的左值
     总结:右值引用变量本身是一个左值 
    */
}

重点:一个右值引用变量本身是左值,因为这个右值引用变量有内存有名字

对Mystring进行优化

提供接受临时变量的拷贝构造和赋值重载

Mystring& operator=(Mystring&&another)
    {
        cout<<"Mystring& operator=(const Mystring&&another)"<<endl;
        this->ptr = another.ptr;
        another.ptr = nullptr;
        return *this;
    }
    
Mystring(Mystring&& another)        //带右值引用参数的拷贝构造
    {
        cout<<"Mystring(Mystring&& another) "<<endl;
        this->ptr = another.ptr;       //将临时对象所托管的内存变为this对象所托管的内存
        another.ptr = nullptr;         //这是必要的,因为临时对象在执行完这之后要析构,若不制空则会将已转移的资源free掉,最终会导致double free
    }

将临时对象的资源移动到当前对象下,避免了数据的拷贝(与没有提供右值引用的方法相比)。右值引用提高了效率

Mystring在operator+的优化

回顾下我们在之前写的 Mystring 中设计的字符串拼接问题

Mystring(const char* str = nullptr)
{
    cout<<"Mystring"<<endl;
    if(str == nullptr)
    {
        ptr = new char('0');
    }
    else
    {
        ptr = new char[sizeof(str) + 1];
        strcpy(ptr, str);
    }
}
Mystring operator+(const Mystring& lptr,const Mystring&hptr)
{
    char* tem = new char[strlen(lptr.ptr) + strlen(hptr.ptr) + 1];
    //tem是函数开辟栈中的一个变量,它指向一块用于字符串连接的内存
    strcpy(tem , lptr.ptr);
    strcat(tem , hptr.ptr);
    //在tem指向的内存中完成了字符串的拼接
    return Mystring(tem);
    //调用Mystring的构造函数,将tem中的内容拷贝至临时对象Mystring中
    //之后栈消失,tem变量不复存在,但是tem指向的内存还存在,这样就导致了内存泄露
}

这样写是肯定有问题的。这里 tem 指向的内存将没有释放的机会

解决方法:

Mystring operator+(const Mystring& lptr,const Mystring&hptr)
{
    char* tem = new char[strlen(lptr.ptr) + strlen(hptr.ptr) + 1];
    strcpy(tem , lptr.ptr);
    strcat(tem , hptr.ptr);
    //第一次内存的拷贝
    Mystring t = Mystring(tem);
    //第二次内存的拷贝
    delete[] tem;
    return t;  //返回对象而不是临时对象,在生成临时对象的时候会有一次拷贝
}

虽然解决了,但是效率有点太低了

优化:

Mystring operator+(const Mystring& lptr,const Mystring&hptr)
{
    Mystring t;  	//生成一个对象直接对对象进行操作
    t.ptr = new char[strlen(lptr.ptr) + strlen(hptr.ptr) + 1];
    strcpy(t.ptr,lptr.ptr);
    strcat(t.ptr,hptr.ptr);
    return t;
}

Mystring中vector的应用

int main()
{
    vector<Mystring> vm;
    vm.reserve(10);
    Mystring str1 = "aaa";
    cout<<"---------------------"<<endl;
    vm.push_back(str1);

    vm.push_back(Mystring("bbb"));
    cout<<"------------------------"<<endl;

    return 0;
}
输出:
Mystring
---------------------
Mystring(const Mystring& another)      //调用拷贝构造
Mystring
Mystring(Mystring&& another)           //调用临时对象的拷贝构造
~Mystring
------------------------
~Mystring
~Mystring
~Mystring

那么push_back的底层是如何实现的呢?

move移动语义和forword类型的完美转化

move模板的实现:

template<typename _Tp>
    _GLIBCXX_NODISCARD
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

可以看到是将传入的变量强转为右值引用变量

实现vector与Mystring的结合:

class Mystring
{
public:
    Mystring(const char* str = nullptr)
    {
        cout<<"Mystring"<<endl;
        if(str == nullptr)
        {
            ptr = new char('0');
        }
        else
        {
            ptr = new char[sizeof(str) + 1];
            strcpy(ptr, str);
        }
    }
    ~Mystring()
    {
        cout<<"~Mystring"<<endl;
        delete[] ptr;
        ptr = nullptr;
    }
    Mystring(const Mystring& another)    //带左值引用参数的拷贝构造
    {
        cout<<"Mystring(const Mystring& another)"<<endl;
        ptr = new char[strlen(another.ptr)  + 1];
        strcpy(ptr,another.ptr);
    }

    Mystring(Mystring&& another)        //带右值引用参数的拷贝构造
    {
        cout<<"Mystring(Mystring&& another) "<<endl;
        this->ptr = another.ptr;       //将临时对象所托管的内存变为this对象所托管的内存
        another.ptr = nullptr;         //这是必要的,因为临时对象在执行完这之后要析构,若不制空则会将已转移的资源free掉,最终会导致double free
    }
    Mystring& operator=(const Mystring&another)
    {
        cout<<"Mystring& operator=(const Mystring&another)"<<endl;
        if(this == &another)
            //this 和 &another 是地址值可以比较
            //*this 和 another 是Mystring的对象,这两个比较要重载 == 运算符
            return *this;
        delete[] this->ptr;
        this->ptr = new char[strlen(another.ptr) + 1];
        strcpy(ptr,another.ptr);
        return *this;
    }
    Mystring& operator=(Mystring&&another)
    {
        cout<<"Mystring& operator=(const Mystring&&another)"<<endl;
        this->ptr = another.ptr;
        another.ptr = nullptr;
        return *this;
    }
    const char* c_str()
    {
        return ptr;
    }
    friend Mystring operator+(const Mystring& lptr,const Mystring&hptr);
    friend ostream& operator<<(ostream& out,Mystring & another);
private:
    char* ptr;
};


template<typename T>
class Allocator
{
public:
    T* allocate(size_t size)  //负责内存开辟
    {
        return (T*) malloc(sizeof(T) * size);
    }
    void deallocate(void *p)  //负责内存释放
    {
        free(p);
    }
    void construct(T*p,const T&val)  //负责对象构造
    {
        new(p) T(val);  //定位new,在p的内存上调用拷贝构造
    }
    void construct(T*p,T&& val)
    {
        new(p) T(std::move(val));
        //右值引用变量本身是左值,这里使用move移动语义构造返回一个临时对象
        //用于调用T()构造函数的参数为临时对象的重载函数
    }
    void destroy(T* p)
    {
        p->~T();
    }
};

template<typename T>
class vector
{
public:
    vector<T>(int size = 10)
    {
        first_ = allocator_.allocate(size);
        last_  = first_;
        end_ = first_ + size + 1;
    }
    ~vector<T>()
    {
        for(T* p=first_;p!=last_;p++)
        {
            allocator_.destroy(p);
        }
        allocator_.deallocate(first_);
        first_ = last_ = end_ = nullptr;
    }
    vector<T>(const vector<T>&another)
    {
        int len = another.last_ - another.first_;
        //len是有效元素的长度
        first_ = allocator_.allocate(len);
        for(int i=0;i<len;i++)
        {
            allocator_.construct(first_+i,another.first_[i]);
        }
        last_ = first_ = len;
        end_ = first_ + (end_ - first_);
    }
    vector&operator=(const vector<T>&another)
    {
        if(this == &another)
            return *this;
        for(T* p=first_;p!=last_;p++)
        {
            allocator_.destroy(p);
        }
        allocator_.deallocate(first_);
        int len = another.last_ - another.first_;
        //len是有效元素的长度
        first_ = allocator_.allocate(len);
        for(int i=0;i<len;i++)
        {
            allocator_.construct(first_+i,(another.first_)[i]);
        }
        last_ = first_ = len;
        end_ = first_ + (end_ - first_);
    }
    bool full() const
    {
        if(last_ == end_)
            return true;
        else
            return false;
    }
    bool empty() const
    {
        return !full();
    }


    void pop_back()
    {
        if (empty())
            throw "vector is empty";
        else
            allocator_.destroy(--last_);
    }
    T back()  //返回容器末尾的元素值
    {
        return *(last_-1);
    }

    void push_back(const T& val)
    {
        if(full())
            expand();
        allocator_.construct(last_,val);
    }
    void push_back(T&& val)
    {
        if(full())
            expand();
        allocator_.construct(last_,std::move(val));
        //右值引用本身是一个左值变量,使用std::move()返回一个右值引用变量,
        // 这里用于调用allocator_.construct()函数的右值引用函数重载
    }
private:
    T* first_;  //指向有效元素的第一个
    T* last_;   //指向最后一个有效元素的后继位置
    T* end_;    //指向数组空间的后继位置
    Allocator<T> allocator_;

    void expand()
    {
        int size = end_ - first_;
        T* tem = allocator_.allocate(size * 2);
        for(int i=0;i<size;i++)
        {
//            tem[i] = first_[i];
            allocator_.construct(tem+i, first_[i]);
        }
        for(int i=0;i<size;i++)
        {
            allocator_.destroy(first_ + i);
        }
        allocator_.deallocate(first_);
        first_ = tem;
        last_ = first_ + size;
        end_ = first_ + size * 2;
    }
};


Mystring operator+(const Mystring& lptr,const Mystring&hptr)
{
    Mystring t;
    t.ptr = new char[strlen(lptr.ptr) + strlen(hptr.ptr) + 1];
    strcpy(t.ptr,lptr.ptr);
    strcat(t.ptr,hptr.ptr);
    return t;
}
ostream& operator<<(ostream& out,Mystring & another)
{
    out<<another.ptr;
    return out;
}

Mystring GetString(Mystring& str)
{
    const char* pstr = str.c_str();
    return Mystring(pstr);        //构造   ->   拷贝
}


int main()
{
    vector<Mystring> vm;
    Mystring str1 = "aaa";
    cout<<"---------------------"<<endl;
    vm.push_back(str1);
    cout<<"---------------------"<<endl;
    vm.push_back(Mystring("bbb"));
    cout<<"------------------------"<<endl;

    return 0;
}

上面的办法有些麻烦。主要是在传递参数的时候要将左值类型通过std::move转化为右值。为了解决之一问题有了引用折叠

将push_bask用函数模板替代:

    template<typename Ty>
    void push_bash(Ty&& val)
    {
        if(full())
            expand();
        allocator_.construct(last_,val);
    }

push_bash(Mystring("abc")); 模板会进行实参推演,将Ty自动替换为 Mystring&& 这样函数就变为void push_bash(Mystring&& && val) 根据引用折叠这等价于void push_bash(Mystring&& val)

Mystring s1("abcd");
push_back(s1); 模板会进行实参推演,将Ty自动替换为 Mystring& 这样函数就变为了void push_bash(Mystring & && val) 根据引用折叠这等价于void push_bash(Mystring& val)

但是,模板中调用了allocator_.construct(last_,val) 函数,右值引用变量其实是一个左值变量,使用allocator_.construct(last_,val);函数调用的是左值引用的重载函数。这与我们想要的结果不同,我们希望的是,当传入左值引用变量的时候allocator_.construct(last_,val);函数调用的是左值引用重载的函数,当传入右值引用参数的时候调用右值引用参数的重载函数

这就引出了,forward 的类型完美转发

    template<typename Ty>
    void construct(T*p,Ty &&val)
    {
        new(p) T(std::forward<Ty>(val));
        //Ty可能是一个左值也可能是一个右值
    }
    
    template<typename Ty>
    void push_back(Ty&& val)
    {
        if(full())
            expand();
        allocator_.construct(last_,std::forward<Ty>(val));
    }

forward类型完美转发,可以识别变量本身是左值还是右值,若本身是左值就返回一个左值引用,若本身是右值变量就返回一个右值引用move移动语义,得到右值类型

最终代码:

#include <iostream>
#include "string.h"
#include "typeinfo"
using namespace std;
#ifdef __glibcxx_assertaa
class test
{
public:
    test(int a=10) : ma(a){cout<<"test()"<<endl;}
    ~test(){cout<<"~test()"<<endl;}
    test(const test& t) :ma(t.ma){cout<<"const test*"<<endl;}
    test& operator=(const test&t)
    {
        cout<<"operator="<<endl;
        this->ma = t.ma;
        return *this;
    }
    int getdata() const
    {
        return ma;
    }
private:
    int ma;
};
test GetObjct(test& t)
{
    int val = t.getdata();
    test tem = test(val);
    //调用构造函数生成临时对象,将临时对象operator=赋值给tem
    return tem;
    //return test(val);
}

int main()
{
    test t1;
    cout<<"------------------"<<endl;
    test t2 = GetObjct(t1);   //用临时对象为唯一个新对象初始化
    cout<<"----------------"<<endl;
    return 0;

}
#endif

class Mystring
{
public:
    Mystring(const char* str = nullptr)
    {
        cout<<"Mystring"<<endl;
        if(str == nullptr)
        {
            ptr = new char('0');
        }
        else
        {
            ptr = new char[sizeof(str) + 1];
            strcpy(ptr, str);
        }
    }
    ~Mystring()
    {
        cout<<"~Mystring"<<endl;
        delete[] ptr;
        ptr = nullptr;
    }
    Mystring(const Mystring& another)    //带左值引用参数的拷贝构造
    {
        cout<<"Mystring(const Mystring& another)"<<endl;
        ptr = new char[strlen(another.ptr)  + 1];
        strcpy(ptr,another.ptr);
    }

    Mystring(Mystring&& another)        //带右值引用参数的拷贝构造
    {
        cout<<"Mystring(Mystring&& another) "<<endl;
        this->ptr = another.ptr;       //将临时对象所托管的内存变为this对象所托管的内存
        another.ptr = nullptr;         //这是必要的,因为临时对象在执行完这之后要析构,若不制空则会将已转移的资源free掉,最终会导致double free
    }
    Mystring& operator=(const Mystring&another)
    {
        cout<<"Mystring& operator=(const Mystring&another)"<<endl;
        if(this == &another)
            //this 和 &another 是地址值可以比较
            //*this 和 another 是Mystring的对象,这两个比较要重载 == 运算符
            return *this;
        delete[] this->ptr;
        this->ptr = new char[strlen(another.ptr) + 1];
        strcpy(ptr,another.ptr);
        return *this;
    }
    Mystring& operator=(Mystring&&another)
    {
        cout<<"Mystring& operator=(const Mystring&&another)"<<endl;
        this->ptr = another.ptr;
        another.ptr = nullptr;
        return *this;
    }
    const char* c_str()
    {
        return ptr;
    }
    friend Mystring operator+(const Mystring& lptr,const Mystring&hptr);
    friend ostream& operator<<(ostream& out,Mystring & another);
private:
    char* ptr;
};


template<typename T>
class Allocator
{
public:
    T* allocate(size_t size)  //负责内存开辟
    {
        return (T*) malloc(sizeof(T) * size);
    }
    void deallocate(void *p)  //负责内存释放
    {
        free(p);
    }

    template<typename Ty>
    void construct(T*p,Ty &&val)
    {
        new(p) T(std::forward<Ty>(val));
        //Ty可能是一个左值也可能是一个右值
    }

    /*void construct(T*p,const T&val)  //负责对象构造
    {
        new(p) T(val);  //定位new,在p的内存上调用拷贝构造
    }
    void construct(T*p,T&& val)
    {
        new(p) T(std::move(val));
        //右值引用变量本身是左值,这里使用move移动语义构造返回一个临时对象
        //用于调用T()构造函数的参数为临时对象的重载函数
    }*/
    void destroy(T* p)
    {
        p->~T();
    }
};


template<typename T>
class vector
{
public:
    vector<T>(int size = 10)
    {
        first_ = allocator_.allocate(size);
        last_  = first_;
        end_ = first_ + size + 1;
    }
    ~vector<T>()
    {
        for(T* p=first_;p!=last_;p++)
        {
            allocator_.destroy(p);
        }
        allocator_.deallocate(first_);
        first_ = last_ = end_ = nullptr;
    }
    vector<T>(const vector<T>&another)
    {
        int len = another.last_ - another.first_;
        //len是有效元素的长度
        first_ = allocator_.allocate(len);
        for(int i=0;i<len;i++)
        {
            allocator_.template construct<Mystring>(first_+i,another.first_[i]);
        }
        last_ = first_ = len;
        end_ = first_ + (end_ - first_);
    }
    vector&operator=(const vector<T>&another)
    {
        if(this == &another)
            return *this;
        for(T* p=first_;p!=last_;p++)
        {
            allocator_.destroy(p);
        }
        allocator_.deallocate(first_);
        int len = another.last_ - another.first_;
        //len是有效元素的长度
        first_ = allocator_.allocate(len);
        for(int i=0;i<len;i++)
        {
            allocator_.construct(first_+i,(another.first_)[i]);
        }
        last_ = first_ = len;
        end_ = first_ + (end_ - first_);
    }
    bool full() const
    {
        if(last_ == end_)
            return true;
        else
            return false;
    }
    bool empty() const
    {
        return !full();
    }


    void pop_back()
    {
        if (empty())
            throw "vector is empty";
        else
            allocator_.destroy(--last_);
    }
    T back()  //返回容器末尾的元素值
    {
        return *(last_-1);
    }
    template<typename Ty>
    void push_back(Ty&& val)
    {
        if(full())
            expand();
        allocator_. construct(last_,std::forward<Ty>(val));
    }
private:
    T* first_;  //指向有效元素的第一个
    T* last_;   //指向最后一个有效元素的后继位置
    T* end_;    //指向数组空间的后继位置
    Allocator<T> allocator_;

    void expand()
    {
        int size = end_ - first_;
        T* tem = allocator_.allocate(size * 2);
        for(int i=0;i<size;i++)
        {
//            tem[i] = first_[i];
            allocator_.construct(tem+i, first_[i]);
        }
        for(int i=0;i<size;i++)
        {
            allocator_.destroy(first_ + i);
        }
        allocator_.deallocate(first_);
        first_ = tem;
        last_ = first_ + size;
        end_ = first_ + size * 2;
    }
};

Mystring operator+(const Mystring& lptr,const Mystring&hptr)
{
    Mystring t;
    t.ptr = new char[strlen(lptr.ptr) + strlen(hptr.ptr) + 1];
    strcpy(t.ptr,lptr.ptr);
    strcat(t.ptr,hptr.ptr);
    return t;
}

ostream& operator<<(ostream& out,Mystring & another)
{
    out<<another.ptr;
    return out;
}

Mystring GetString(Mystring& str)
{
    const char* pstr = str.c_str();
    return Mystring(pstr);        //构造   ->   拷贝
}

int main()
{
    vector<Mystring> vm;
    Mystring str1 = "aaa";
    cout<<"---------------------"<<endl;
    vm.push_back(str1);
    cout<<"---------------------"<<endl;
    vm.push_back(Mystring("bbb"));
    cout<<"------------------------"<<endl;

    return 0;
}