/*
1.gcc编译运行结果
[root@VM_0_7_centos test]# g++ --std=c++17 mystring.cpp
[root@VM_0_7_centos test]# ./a.out 1
String ctor: null
String ctor: two
String ctor: three
String copy ctor: two
a.c_str() =
b.c_str() = two
d.c_str() = three
e.c_str() = two
ctor_cnt1= 4
String ctor: one
String move ctor: one
String dtor: null
------------
String copy ctor: one
ctor_cnt2= 6
String copy ctor: two
ctor_cnt3= 7
String move ctor: two
ctor_cnt4= 7
String operator= : two
ctor_cnt5= 7
a.c_str() = two
b.c_str() = two
d.c_str() = three
---String g(std::move(a))---
String ctor: null
String move ctor: two
g.c_str() = two
String dtor: two
it.c_str() = one
it.c_str() = two
After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"
String dtor: one
String dtor: two
String dtor: null
String dtor: three
String dtor: two
String dtor: null
ctor_cnt= 8
dtor_cnt= 8
2.VC6.0 运行结果
String ctor: null
String ctor: two
String ctor: three
String copy ctor: three
String dtor: three
String copy ctor: two
a.c_str() =
b.c_str() = two
d.c_str() = three
e.c_str() = two
String dtor: two
String dtor: three
String dtor: two
String dtor:
ctor_cnt= 5
dtor_cnt= 5
Press any key to continue
结论:gcc 默认会开启rov优化,有些可以不用进行拷贝构造函数提高效率
*/
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
int ctor_cnt = 0;
int dtor_cnt = 0;
bool ismove = 0;
class String{
public:
friend ostream& operator<< (ostream&,String&);//重载<<运算符
friend istream& operator>> (istream&,String&);//重载>>运算符
String(const char* ptr = NULL); //通配构造函数
String(const String& str); //拷贝构造函数
String& operator=(const String & str);//=运算符重载
#ifdef __GNUC__
String& operator=(String&& other);
#endif
size_t size() const; //获取字符的长度
const char* c_str();//转化成c字符串
~String(); //析构函数
//private:
public:
char* data; //实际字符串内容
size_t length; //字符串的长度
};
//重载输出运算符
ostream& operator<< (ostream& os,String& str)//重载<<运算符
{
os<<str.data;
return os;
}
//重载输入运算符
istream& operator>> (istream& is ,String& str)//重载>>运算符
{
该用法是错误的is>>str.data;
//因为不能确定str.data和输入的字符串的大小
char tmp[1000] = {0};
is>>tmp;
str.length = strlen(tmp);
str.data = new char[str.length + 1];
strcpy(str.data,tmp);
return is;
}
//通用构造函数
String::String(const char* ptr){
if(!ptr){
data = new char[1];
*data = 0;
length = 0;
cout<<"String ctor: null"<<endl;
}else{
length = strlen(ptr);
data = new char[length + 1];
strcpy(data,ptr);
cout<<"String ctor: "<<ptr<<endl;
}
ctor_cnt++;
}
//拷贝构造函数,需要进行深拷贝
//使用方法:String s2(s1); 使用s1来初始化s2,调用该拷贝构造函数进行深拷贝
String::String(const String& str)
{
if(!str.data){
data = 0;
}else{
length = strlen(str.data);
data = new char[length + 1];
strcpy(data,str.data);
data[length] = 0;
}
ctor_cnt++;
cout<<"String copy ctor: "<<data<<endl;
}
String& String::operator=(const String &str)//赋值操作符4步
{
if (this == &str) return *this;//1 自我赋值,返回自身引用
delete[] data;//2 删除原有数据
length = str.size();//3 深拷贝
data = new char[length + 1];
strcpy(data, str.data);
cout<<"String operator= : "<<data<<endl;
return *this;//4 返回自身引用
}
size_t String::size() const//获取字符的长度
{
return length;
}
const char* String::c_str()//转化成c字符串
{
return data;
}
String::~String() //析构函数
{
if(data){
cout<<"String dtor: "<<data<<endl;
delete []data;
data = NULL;
length = 0;
}
else
{
cout<<"String dtor: null"<<endl;
}
dtor_cnt++;
}
#ifdef __GNUC__
// 简单的移动赋值运算符
String& String::operator=(String&& other)
{
if (other.data)
{
data = std::move(other.data);
length = std::move(other.length);
other.data = 0;
other.length = 0;
}
cout<<"String move ctor: "<<data<<endl;
return *this;
}
#endif
//gcc 默认会进行rov优化,不会调用拷贝构造函数,直接把栈内的f对象提升级调用函数可用对象,避免调用拷贝构造函数及销毁栈内的c对象的效率降低操作
String make_string()
{
String c("three");//gcc-3 VC-3 按基本原理 c对象在make_string退出时就会销毁
return c;//gcc-3 VC-4 这个会发生拷贝构造函数,深度拷贝
}
int test_fun()
{
String a;//gcc-1 VC-1
String b("two");//gcc-2 VC-2
String d = make_string();//gcc-3 VC-4不是返回c对象,而是返回对c对象的拷贝 gcc与VC编译器有没有rov优化主要差别在这句
String e = b;//gcc-4 VC-5
cout <<"a.c_str() = "<<a.c_str()<<endl;
cout <<"b.c_str() = "<<b.c_str()<<endl;
cout <<"d.c_str() = "<<d.c_str()<<endl;
cout <<"e.c_str() = "<<e.c_str()<<endl;
#ifdef __GNUC__
cout<<"ctor_cnt1= "<<ctor_cnt<<endl;
std::vector<String> v2;
v2.reserve(16);//先预分配16个,不然空间不够时,会重新分配和移动原空间,如由1-》2会发生2次拷贝构造函数
a = "one";//这一步会先用创建“one”创建一个临时对象,然后再运行String& String::operator=(String&& other)进行move操作,最后再销毁临时对象
cout<<"------------"<<endl;
v2.push_back(a);//会调用拷贝构造函数
cout<<"ctor_cnt2= "<<ctor_cnt<<endl;
v2.push_back(b);//调用拷贝构造函数 没有v2.reserve(16);这句时,由1——》2会发生2次拷贝构造函数 vector一般每次以2幂次增长
cout<<"ctor_cnt3= "<<ctor_cnt<<endl;
a=std::move(e);//调用String& String::operator=(String&& other)进行move操作,操作完,e的指针为变成空,这行与a = e;是不一样的
cout<<"ctor_cnt4= "<<ctor_cnt<<endl;
b = a; //调用 String& String::operator=(const String &str)进行赋值操作
cout<<"ctor_cnt5= "<<ctor_cnt<<endl;
cout <<"a.c_str() = "<<a.c_str()<<endl;
cout <<"b.c_str() = "<<b.c_str()<<endl;
cout <<"d.c_str() = "<<d.c_str()<<endl;
if (e.c_str())//这个不会打印出来,因为使用了a=std::move(e);会调用String& String::operator=(String&& other)
{
cout <<"e.c_str() = "<<e.c_str()<<endl;
}
std::vector<String> v3;
if (ismove)
{
v3 = std::move(v2);//与v3 = v2;是有差异的,这个是不是创建新的String类,只是把v2中移到v3
}
else
{
v3 = v2;
}
if(ismove)
{
cout<<"---String g(std::move(a))---"<<endl;
String g;
g = std::move(a);
if (a.c_str())//这个不会打印出来,因为使用了a=std::move(e);会调用String& String::operator=(String&& other)
{
cout <<"a.c_str() = "<<a.c_str()<<endl;
}
if (g.c_str())
{
cout <<"g.c_str() = "<<g.c_str()<<endl;
}
}
else
{
cout<<"---String g(a)---"<<endl;
//String g(a);//-->String copy ctor: two
String g(std::move(a));//-->String copy ctor: two 这个不会进行move操作,与String g(a)结果一样,编译器优化掉?
if (a.c_str())
{
cout <<"a.c_str() = "<<a.c_str()<<endl;
}
if (g.c_str())
{
cout <<"g.c_str() = "<<g.c_str()<<endl;
}
}
for (std::vector<String>::iterator it=v3.begin(); it!=v3.end();++it)
{
cout <<"it.c_str() = "<<it->c_str()<<endl;
}
#endif
#ifdef __GNUC__
std::string str = "Hello";
std::vector<std::string> v;
// 使用 push_back(const T&) 重载,
// 表示我们将带来复制 str 的成本
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// 使用右值引用 push_back(T&&) 重载,
// 表示不复制字符串;而是
// str 的内容被移动进 vector
// 这个开销比较低,但也意味着 str 现在可能为空。
// 若是这个对象后面就不再使用,如当前这种情况,可使用std::move(cb)这种方式,这种方式只会调整指针,不过通过这种操作str对象后面就不能再使用了
/*
//C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。
//std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。
//对指针类型的标准库对象并不需要这么做.
*/
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are \"" << v[0]
<< "\", \"" << v[1] << "\"\n";
#endif
return 0;
}
int main(int argc,char **argv)
{
if (argc > 1)
{
ismove = bool(atoi(argv[1])&0x01);
}
test_fun();
cout<<"ctor_cnt= "<<ctor_cnt<<endl;
cout<<"dtor_cnt= "<<dtor_cnt<<endl;
return 0;
}
/*
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
String(const char* str = NULL);//通用构造函数,String("abc")
String(const String &str);//拷贝构造
~String();
String& operator=(const String &str);//赋值运算符。返回引用
String operator+(const String &str) const;
String& operator+=(const String &str);//+=操作符。返回引用
char& operator[](int n) const;//下标操作符。返回引用
bool operator==(const String &str) const;
int size() const;//字符串实际大小,不包括结束符
const char *c_str() const;//将string转为char *
private:
char *data;
int length;
};
String::String(const char* str)//通用构造
{
if (!str)
{//为空。String a()
length = 0;
data = new char[1];
*data = '\0';
}
else
{
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);//会拷贝源的结束符
}
}
String::String(const String &str)//拷贝构造,深拷贝
{
length = str.size();
data = new char[length + 1];
strcpy(data, str.c_str());
}
String::~String()
{
delete[] data;
length = 0;
}
String& String::operator=(const String &str)//赋值操作符4步
{
if (this == &str) return *this;//1 自我赋值,返回自身引用
delete[] data;//2 删除原有数据
length = str.size();//3 深拷贝
data = new char[length + 1];
strcpy(data, str.c_str());
return *this;//4 返回自身引用
}
String String::operator+(const String &str) const//+操作符3步
{//新建对象包括新空间,拷贝两个数据,返回新空间
String newString;
newString.length = length + str.size();
newString.data = new char[newString.length + 1];
strcpy(newString.data, data);
strcat(newString.data, str.data);
return newString;
}
String& String::operator+=(const String &str)//+=操作符5步
{//重分配新空间,拷贝两个数据,删除自己原空间,赋值为新空间,返回引用
length += str.size();//成员length是实际长度
char *newdata = new char[length + 1];
strcpy(newdata, data);
strcat(newdata, str.c_str());
delete[] data;
data = newdata;
return *this;
}
char& String::operator[](int n) const
{//下标操作符,返回引用
if (n >= length) return data[length - 1];//如果越界,返回最后一个字符
else return data[n];
}
bool String::operator==(const String &str) const
{
if (length != str.size()) return false;
return strcmp(data, str.c_str()) false : true;
}
int String::size() const
{
return length;
}
const char *String::c_str() const
{
return data;
}
int main()
{
char a[] = "Hello", b[] = "World!";
String s1(a), s2(b);
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
s1 += s2;
cout << s1.c_str() << endl;
s1 = s2;
cout << s1.c_str() << endl;
cout << (s1 + s2).c_str() << endl;
cout << s1.size() << endl;
cout << s1[1] << endl;
if (s1 == s2)
cout << "相等" << endl;
return 0;
}
*/
/*
//std::move使用示例
//C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。
//std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。
//对指针类型的标准库对象并不需要这么做.
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
// 使用 push_back(const T&) 重载,
// 表示我们将带来复制 str 的成本
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// 使用右值引用 push_back(T&&) 重载,
// 表示不复制字符串;而是
// str 的内容被移动进 vector
// 这个开销比较低,但也意味着 str 现在可能为空。
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are \"" << v[0]
<< "\", \"" << v[1] << "\"\n";
}
*/