1.c++对象的优化
对象被优化后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;
}
总结三条对象优化的规则
- 函数参数传递过程中,对象优先按引用传递,不要按值传递(避免了参数传递中的拷贝过程与函数结束后被拷贝对象的析构过程)
- 当函数返回对象时,要优先返回一个临时对象,而不是返回一个定义过的对象
- 接受返回值是对象的函数调用的时候,应该优先按照初始化的方式接受而不是按照赋值的方式接受
返回对象
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;
}