測試string類作棧內對象返回時其gcc和VC6.0編譯器的處理方式,以及測試move操作

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章