/*
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";
}
*/