亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++類和對象之類的6個(gè)默認(rèn)成員函數(shù)詳解

 更新時(shí)間:2023年02月10日 09:51:25   作者:榶曲  
類是對某一事物的抽象描述,具體地講類是C++中的一種構(gòu)造的數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于C++類和對象之類的6個(gè)默認(rèn)成員函數(shù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

1.類的6個(gè)默認(rèn)成員函數(shù)

默認(rèn)成員函數(shù):用戶沒有顯示實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱為默認(rèn)成員函數(shù)。

如果一個(gè)類中什么成員都沒有,簡稱為空類。

但空類中真的是什么都沒有嗎?并不是的,任何一個(gè)類在我們不寫的情況下,都會(huì)自動(dòng)生成下面6個(gè)默認(rèn)成員函數(shù)。

class Date{};
  • 構(gòu)造函數(shù): 完成初始化工作
  • 析構(gòu)函數(shù): 完成清理工作
  • 拷貝構(gòu)造函數(shù): 使用同類對象初始化創(chuàng)建對象
  • 賦值重載: 把一個(gè)對象賦值給另一個(gè)對象
  • 取地址操作符重載: 對普通對象取地址
  • const取地址操作符重載: 對const修飾的對象取地址

2.構(gòu)造函數(shù)

2.1概念

對于下面的Date類:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date today1;
	today1.Init(2023,1,16);
	today1.Print();

	Date today2;
	today2.Init(2023, 1, 17);
	today2.Print();

	return 0;
}

對于Date類,可以通過Init公有方法給對象設(shè)置日期,但如果每次創(chuàng)建對象時(shí)都調(diào)用該方法設(shè)置信息,未免有點(diǎn)麻煩,那能否在對象創(chuàng)建時(shí),就將信息設(shè)置進(jìn)去呢?

我們就需要一個(gè)函數(shù):保證對象被創(chuàng)造出來就被初始化了。

C++的構(gòu)造函數(shù)提供了這個(gè)功能:

構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值,并且在對象整個(gè)生命周期內(nèi)只調(diào)用一次。

2.2特性

構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任務(wù)并不是開空間創(chuàng)建對象,而是初始化對象。

其特征如下:

  1. 函數(shù)名與類名相同。
  2. 無返回值(也不用寫void)
  3. 對象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對應(yīng)的構(gòu)造函數(shù)。

構(gòu)造函數(shù)可以重載。(一個(gè)類可以有多個(gè)構(gòu)造函數(shù))

class Date
{
public:
	Date()
	{
		cout << "自定義默認(rèn)構(gòu)造函數(shù)" << endl;
	}
	//Date(int year = 1, int month= 2, int day = 3)
	//{
	//	cout << "自定義全缺省默認(rèn)構(gòu)造函數(shù)" << endl;
	//}
	//Date(int year, int month, int day = 1)
	//{
	//	cout << "自定義半缺省構(gòu)造函數(shù)" << endl;
	//}
	Date(int year, int month, int day)
	{
		cout << "自定義構(gòu)造函數(shù)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date today(2023, 2, 6);

	return 0;
}

無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)一個(gè)類中只能有一個(gè)。

原因: 這兩個(gè)構(gòu)造函數(shù)雖然滿足重載,但編譯器無法調(diào)用,存在歧義。如上面代碼的第一個(gè)無參構(gòu)造函數(shù)和第二個(gè)注釋的全缺省的構(gòu)造函數(shù),所以默認(rèn)構(gòu)造函數(shù)一個(gè)類只能有一個(gè)。(非默認(rèn)構(gòu)造函數(shù)也只能有一個(gè),如第三個(gè)半缺省構(gòu)造函數(shù)和第四個(gè)構(gòu)造函數(shù),需要傳參,同時(shí)存在會(huì)有歧義)

注意: 更具構(gòu)造函數(shù)需不需要傳參數(shù),我們將其分為兩種

  • 默認(rèn)構(gòu)造函數(shù): 無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù),我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù)(下一條)(這些不需要傳參數(shù)的構(gòu)造函數(shù),都認(rèn)為是默認(rèn)構(gòu)造函數(shù))
  • 傳參構(gòu)造函數(shù): 不缺省構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)
    • 創(chuàng)建對象時(shí),調(diào)用默認(rèn)構(gòu)造函數(shù)不要在對象后加括號(hào)(加括號(hào)后編譯器會(huì)將其看作函數(shù)的聲明,而不是創(chuàng)建的對象)。調(diào)用傳參的構(gòu)造函數(shù)在對象后加括號(hào)加參數(shù)。

如果類沒有顯示定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯示定義編譯器將不再生成。(構(gòu)造函數(shù)可以重載,有很多種,只要我們寫一種,編譯器就不會(huì)默認(rèn)生成構(gòu)造函數(shù))

注意: 如下,創(chuàng)建對象時(shí)不帶括號(hào),調(diào)用的是默認(rèn)構(gòu)造函數(shù),帶括號(hào)后跟參數(shù),調(diào)用傳參構(gòu)造函數(shù)。

如下圖類中已經(jīng)定義了構(gòu)造函數(shù),編譯器不會(huì)在自動(dòng)生成默認(rèn)構(gòu)造函數(shù)。

在增加默認(rèn)構(gòu)造函數(shù)后,正常運(yùn)行

關(guān)于編譯器生成的默認(rèn)構(gòu)造函數(shù),很多人會(huì)疑惑:不實(shí)現(xiàn)構(gòu)造函數(shù)的情況下,編譯器會(huì)生成默認(rèn)的構(gòu)造函數(shù)。但是看起來默認(rèn)構(gòu)造函數(shù)又沒什么用?

如下面的代碼,today對象調(diào)用了編譯器生成的默認(rèn)構(gòu)造函數(shù),但是today對象的三個(gè)成員變量_day/_month/_year,依然是隨機(jī)數(shù),也就是說在這里編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒有什么用?

先介紹一點(diǎn),C++將類型分為以下兩種:

  • 內(nèi)置類型: 語言提供的數(shù)據(jù)類型,如:int、char…
  • 自定義類型: 我們使用struct、class、union等自己定義的類型

如果一個(gè)類中存在自定義類型的成員變量,需要使用該成員變量對應(yīng)類的默認(rèn)構(gòu)造函數(shù)來初始化,否則無法通過。這也就是默認(rèn)構(gòu)造函數(shù)存在的意義。

自定義類型的成員變量對應(yīng)類存在默認(rèn)構(gòu)造函數(shù)

class A
{
public:
	A()
	{
		cout << "A" << endl;
	}
private:
	int a;
	int b;
};

class Date
{
public:
	Date()
	{
		cout << "默認(rèn)構(gòu)造函數(shù)" << endl;
	}
	Date(int year, int month, int day)
	{
		cout << "傳參構(gòu)造函數(shù)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	A a1;
};

int main()
{
	Date today;

	return 0;
}

自定義類型的成員變量對應(yīng)類不存在默認(rèn)構(gòu)造函數(shù)

class A
{
public:
	A(int c)
	{
		cout << "A" << endl;
	}
private:
	int a;
	int b;
};

class Date
{
public:
	Date()
	{
		cout << "默認(rèn)構(gòu)造函數(shù)" << endl;
	}
	Date(int year, int month, int day)
	{
		cout << "傳參構(gòu)造函數(shù)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	A a1;
};

int main()
{
	Date today;

	return 0;
}

注意: 在C++11中針對內(nèi)置成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值。

class A
{
public:
	void Print()
	{
		cout << _a << " " << _b << endl;
	}
private:
	int _a = 10;
	int _b = 20;
};

class Date
{
public:
	void Print()
	{
		a1.Print();
	}

private:
	int _year = 2000;
	int _month = 1;
	int _day = 1;
	A a1;
};
int main()
{
	Date today;
	today.Print();

	return 0;
}

此為缺省值,當(dāng)構(gòu)造函數(shù)沒有初始化成員變量時(shí),成員變量的值即為該缺省值,若初始化,以構(gòu)造函數(shù)為主(如下面代碼,初始化了一個(gè)變量,該變量就以構(gòu)造函數(shù)初始化為主,其他成員變量為缺省值)

class A
{
public:
	A()
	{
		_a = 40;
	}
	void Print()
	{
		cout << _a << " " << _b << endl;
	}
private:
	int _a = 10;
	int _b = 20;
};

class Date
{
public:
	void Print()
	{
		a1.Print();
	}

private:
	int _year = 2000;
	int _month = 1;
	int _day = 1;
	A a1;
};
int main()
{
	Date today;
	today.Print();

	return 0;
}

3.析構(gòu)函數(shù)

3.1概念

我們知道了對象創(chuàng)建時(shí)需要構(gòu)造函數(shù)來初始化,那對象銷毀時(shí)又需要什么呢?

析構(gòu)函數(shù): 與構(gòu)造函數(shù)相反,析構(gòu)函數(shù)不是完成對對象本身的銷毀,局部對象銷毀工作是由編譯器完成的。而對象在銷毀時(shí)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對象中資源的清理工作。

我們創(chuàng)建一個(gè)對象,它是在對象生命周期結(jié)束后,對應(yīng)函數(shù)的棧幀銷毀時(shí)一并銷毀,而析構(gòu)函數(shù)是在銷毀前函數(shù)自動(dòng)調(diào)用,對該對象的資源做清理清空對象的空間或?qū)⑸暾埖目臻g還給編譯器。

對于清理工作,我們必須要做,否則可能會(huì)造成內(nèi)存泄漏,而我們又常常忘記這一操作,于是C++增加了這么一個(gè)函數(shù)。

3.2特性

  1. 析構(gòu)函數(shù)名是在類名前加上字符**~**(取反符號(hào))
  2. 無參數(shù)也無返回值
  3. 一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯示定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:析構(gòu)函數(shù)不能重載
  4. 對象生命周期結(jié)束時(shí),C++編譯系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。

我們編寫如下代碼,向內(nèi)存申請空間,利用析構(gòu)函數(shù)釋放對應(yīng)的空間。

class Stack
{
public:
	Stack()
	{
		ArrStack = (int*)malloc(sizeof(int) * 4);
        if(!ArrStack)//下圖中未寫
        {
            preeor("malloc fail!");
            exit(-1);
        }
		_size = 4;
		_top = 0;
	}
	~Stack()
	{
		if (ArrStack)
		{
			free(ArrStack);
			ArrStack = nullptr;
			_size = 0;
			_top = 0;
		}
	}
private:
	int* ArrStack;
	int _size;
	int _top;
};

int main()
{
	Stack st;

	return 0;
}

如果類中沒有申請資源時(shí),析構(gòu)函數(shù)可以不寫,直接使用編譯器生成默認(rèn)析構(gòu)函數(shù),比如Date類;有資源申請時(shí),一定要寫,否則會(huì)造成資源泄漏。

如下面的代碼,當(dāng)我們對同一個(gè)類創(chuàng)建兩個(gè)變量時(shí),構(gòu)造函數(shù)的執(zhí)行順序?yàn)椋簊1、s2,而函數(shù)是一種棧的形式,創(chuàng)建變量就是壓棧,s1先入棧,s2后入棧,銷毀時(shí),s2先出棧,s1后出棧,析構(gòu)函數(shù)的調(diào)用順序?yàn)椋簊2、s1

class Stack
{
public:
	Stack(int num)
	{
		ArrStack = (int*)malloc(sizeof(int) * num);
        if(!ArrStack)//下圖中未寫
        {
            preeor("malloc fail!");
            exit(-1);
        }
		_size = 4;
		_top = 0;
	}
	~Stack()
	{
		if (ArrStack)
		{
			free(ArrStack);
			ArrStack = nullptr;
			_size = 0;
			_top = 0;
		}
	}
private:
	int* ArrStack;
	int _size;
	int _top;
};

int main()
{
	Stack s1(10);
	Stack s1(40);

	return 0;
}

觀察下圖this->_size的變化

當(dāng)一個(gè)類中有自定義類型的成員變量,那再銷毀這個(gè)類創(chuàng)建的對象時(shí),會(huì)調(diào)用該類中自定義類型的成員變量的析構(gòu)函數(shù)

寫析構(gòu)函數(shù)

class A
{
public:
	~A()
	{
		cout << "A" << endl;
	}
private:
	int a;
	int b;
};

class Date
{
public:
	~Date()
	{
		cout << "Date" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	A a1;
};
int main()
{
	Date today;

	return 0;
}

不寫析構(gòu)函數(shù)

class A
{
public:
	~A()
	{
		cout << "A" << endl;
	}
private:
	int a;
	int b;
};

class Date
{
public:
	
private:
	int _year;
	int _month;
	int _day;
	A a1;
};
int main()
{
	Date today;

	return 0;
}

注意:

  • 默認(rèn)生成構(gòu)造函數(shù)和默認(rèn)生成析構(gòu)函數(shù),對內(nèi)置類型不處理,處理自定義類型。(有些編譯器會(huì),但那時(shí)編譯器的個(gè)人行為,和C++的語法無關(guān))

4.拷貝構(gòu)造函數(shù)

4.1概念

拷貝構(gòu)造函數(shù): 只有單個(gè)形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創(chuàng)建新對象時(shí)由編譯器自動(dòng)調(diào)用。

該函數(shù)功能為將一個(gè)對象的數(shù)據(jù)賦值給另一個(gè)對象,發(fā)生拷貝時(shí)編譯器就會(huì)調(diào)用該函數(shù),如下:

class Date
{
public:
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)//拷貝構(gòu)造函數(shù)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		cout << "拷貝構(gòu)造函數(shù)" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

void test(Date d)//調(diào)用拷貝構(gòu)造函數(shù)
{}

int main()
{
	Date today1(2023,2,7);
	Date today2(today1);//調(diào)用拷貝構(gòu)造函數(shù)

	test(today1);

	return 0;
}

4.2特征

拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。

拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)必須是類類型對象的引用 ,使用傳參方式編譯器會(huì)直接報(bào)錯(cuò) ,因?yàn)闀?huì)引發(fā)無窮遞歸調(diào)用。

如果不使用引用,代碼如下:

class Date
{
public:
	Date(const Date d)//拷貝構(gòu)造函數(shù)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};

這樣的拷貝構(gòu)造函數(shù),我們在調(diào)用它時(shí)會(huì)發(fā)生拷貝,而需要拷貝我們就要調(diào)用拷貝構(gòu)造函數(shù),這就會(huì)形參死循環(huán),因?yàn)橐媚阄艺{(diào)用你,而想要調(diào)用你就要用你,編譯器不會(huì)允許這樣的事情發(fā)生。

如上圖,將對象d1的數(shù)據(jù)拷貝到d2,需要調(diào)用拷貝構(gòu)造函數(shù),而調(diào)用的過程形參發(fā)生拷貝又要調(diào)用拷貝構(gòu)造函數(shù),就這樣一直下去,很明顯這是不行的。

所以在這里我們要使用引用,如下:

	Date(const Date& d)//拷貝構(gòu)造函數(shù)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

在第一次調(diào)用的時(shí)候,使用d給對象起別名,就不用再調(diào)用其他拷貝構(gòu)造函數(shù)。

對于這個(gè)函數(shù)建議使用const修飾,防止我們在寫這個(gè)函數(shù)時(shí)不小心寫錯(cuò),使對象的成員變量發(fā)生改變。

**若未顯示定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。**默認(rèn)的拷貝構(gòu)造函數(shù)對象按內(nèi)存存儲(chǔ)按字節(jié)序完成拷貝,這種拷貝叫淺拷貝,或值拷貝。

即上面的Date類對象,若發(fā)生淺拷貝只是將一個(gè)對象所占空間內(nèi)所有成員變量的值拷貝到另一個(gè)對象的成員變量,這么做看起來似乎很合理其實(shí)不然,對于內(nèi)置類型,這么做當(dāng)然沒有問題,但如棧這樣的數(shù)據(jù)結(jié)構(gòu),是萬萬不能的。如下面棧的代碼

class Stack
{
public:
    Stack(size_t capacity = 10)
    {
        _array = (int*)malloc(int* sizeof(int));
        if (nullptr == _array)
        {
            perror("malloc申請空間失敗");
            return;
        }
        _size = 0;
        _capacity = capacity;
    }
    void Push(const DataType& data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }
    ~Stack()
    {
        if (_array)
        {
            free(_array);
            _array = nullptr;
            _capacity = 0;
            _size = 0;
        }
    }
private:
    int *_array;
    size_t _size;
    size_t _capacity;
};
int main()
{
    Stack s1;
    s1.Push(1);
    s1.Push(2);
    s1.Push(3);
    s1.Push(4);
    Stack s2(s1);
    return 0;
}

這樣程序必定會(huì)發(fā)生錯(cuò)誤。

如果想要讓程序正確運(yùn)行我們,需要我們自己編寫拷貝構(gòu)造函數(shù),也就是深拷貝,讓他們每個(gè)對象的的成員變量在面對這種情況時(shí)都有自己獨(dú)立的空間,而不是共用一塊空間。

這也是拷貝構(gòu)造函數(shù)存在的意義,編譯器只能做淺拷貝的工作,若果一個(gè)對象的拷貝需要使用深拷貝,就需要程序員手動(dòng)來完成這個(gè)任務(wù),這也是C語言存在的缺陷,C++的很好的彌補(bǔ)了這一點(diǎn)。

修改后的棧代碼如下:

class Stack
{
public:
    Stack(size_t capacity = 10)
    {
        _array = (int*)malloc(capacity * sizeof(int));
        if (nullptr == _array)
        {
            perror("malloc申請空間失敗");
            return;
        }
        _size = 0;
        _capacity = capacity;
    }
    Stack(const Stack& st)
    {
        _array = (int*)malloc(sizeof(int) * st._capacity);
        if (_array == nullptr)
        {
            perror("malloc申請空間失敗");
            return;
        }
        for (int i = 0; i < st._size; i++)
        {
            _array[i] = st._array[i];
        }
        _size = st._size;
    }
    void Push(const int& data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }
    ~Stack()
    {
        if (_array)
        {
            free(_array);
            _array = nullptr;
            _capacity = 0;
            _size = 0;
        }
    }
private:
    int* _array;
    size_t _size;
    size_t _capacity;
};
int main()
{
    Stack s1;
    s1.Push(1);
    s1.Push(2);
    s1.Push(3);
    s1.Push(4);
    Stack s2(s1);
    return 0;
}

所以如果類中沒有涉及資源申請時(shí),拷貝構(gòu)造函數(shù)是否寫都可以,若涉及到資源申請時(shí),則拷貝構(gòu)造函數(shù)是一定要寫的,否則就是淺拷貝。

拷貝構(gòu)造函數(shù)調(diào)用頻率最多的三種場景場景如下

  • 使用以存在的對象創(chuàng)建新對象
  • 函數(shù)參數(shù)類型為類類型對象
  • 函數(shù)返回值類型為類類型對象

通過這些我們也可以看出,拷貝在編寫代碼中是一個(gè)平常的事情,但其消耗的資源卻不少,所以在實(shí)際使用中,如果可以使用引用,盡量使用引用,減少計(jì)算機(jī)消耗,創(chuàng)出更優(yōu)得程序。

5.賦值運(yùn)算符重載

5.1運(yùn)算符重載

C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù), 也具有其返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。

在C++中類的封裝性是做的很好的,如果想在類和類之間進(jìn)行比較,拷貝等操作需要在類內(nèi)調(diào)用函數(shù),而對應(yīng)普通的內(nèi)置類型,只需要使用簡單的運(yùn)算符即可完成,C++規(guī)定可以將部分運(yùn)算符重載來完成這個(gè)功能,增強(qiáng)了代碼的可讀性。

函數(shù)名字為:關(guān)鍵字operator后面接需要重載的運(yùn)算符符號(hào)。

函數(shù)原型:返回值類型 operator操作符(參數(shù)列表)

注意:

  • 不能通過連接其他符號(hào)來創(chuàng)建新的操作符:比如operator@
  • 重載操作符必須有一個(gè)類類型參數(shù)
  • 用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整形+,不能改變其含義
  • 作為類成員函數(shù)重載時(shí),其形參看起來比操作數(shù)數(shù)目少1.因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this
  • .* :: sizeof ?: .注意以上5個(gè)運(yùn)算符不能重載。在這個(gè)經(jīng)常在筆試選擇題中出現(xiàn)。

如下代碼,若運(yùn)算符重載函數(shù)作用域?yàn)槿?,那類的成員變量必須為公有的,這樣封裝性就無法保證

class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//成員變量變?yōu)楣校拍苁诡愅庠L問
//private:
	int _year;
	int _month;
	int _day;
};

bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year && d1._month == d2._month
		&& d1._day == d2._day;
}

bool test()
{
	Date today1(2023, 2, 7);
	Date today2;
	return today1 == today2;
}

這里我們可以使用友元解決,也可以將運(yùn)算符重載函數(shù)放入類中,我們一般將其放入類中。

class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d1)
	{
		return _year == d1._year && _month == d1._month
			&& _day == d1._day;
	}

private:
	int _year;
	int _month;
	int _day;
};

bool test()
{
	Date today1(2023, 2, 7);
	Date today2;
    //today1.operator==(today2)
	return today1 == today2;
}

在調(diào)用成員函數(shù)時(shí),編譯器會(huì)自動(dòng)將調(diào)用的對象作為this指針傳遞,我們只要寫入一個(gè)參數(shù)即可。

注意:

在使用時(shí)需要注意運(yùn)算符優(yōu)先級,如下面使用運(yùn)算符重載需使用括號(hào)

	cout << (today1 == today2) << endl;

運(yùn)算符重載中,如果有多個(gè)參數(shù),第一參數(shù)為左操作數(shù),第二個(gè)參數(shù)為右操作數(shù),以此類推。如上面的代碼,第一個(gè)參數(shù)為today1,為左操作數(shù),由該對象調(diào)用運(yùn)算符重載函數(shù),第二參數(shù)today2即為參數(shù)。

5.2賦值運(yùn)算符重載

賦值運(yùn)算符如果不自己實(shí)現(xiàn),編譯器會(huì)默認(rèn)生成,只有賦值取地址是這樣的,其它的自定義類型需要使用,就要我們自己寫。(取地址在下面)

賦值運(yùn)算符重載格式:

  • 參數(shù)類型: const Typedef&,傳遞引用可以提高傳參效率
  • 返回值類型: Typedef&,返回引用可以提高返回得效率,有返回值目的是為了支持連續(xù)賦值。
  • 檢測是否自己給自己賦值
  • **返回*this:**要符合連續(xù)賦值得含義
class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)//檢測是否自己給自己賦值
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;//返回*this
	}

private:
	int _year;
	int _month;
	int _day;
};

賦值運(yùn)算符只能重載成類得成員函數(shù)不能重載成全局函數(shù)

class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//private:
	int _year;
	int _month;
	int _day;
};

//全局函數(shù)不能用`this`指針,需要給兩個(gè)參數(shù)
Date& operator=(const Date& d1, const Date& d2)
{
    if (d1 != &d2)//檢測是否自己給自己賦值
    {
        d1._year = d2._year;
        d1._month = d2._month;
        d1._day = d2._day;
    }
	return d1;//返回*this
}

其中為了訪問類中得成員變量,將其公有化,失去了封裝性。這樣得函數(shù)注定編譯失敗,其中賦值運(yùn)算符沒有實(shí)現(xiàn),則編譯器會(huì)在類中自己實(shí)現(xiàn)一個(gè)默認(rèn)的賦值運(yùn)算符,而在調(diào)用得時(shí)候,我們自己實(shí)現(xiàn)了一個(gè),編譯器又實(shí)現(xiàn)了一個(gè)這就產(chǎn)生沖突。

所以,賦值運(yùn)算符重載只能是類的成員函數(shù)。

上面已經(jīng)講了,如果我們沒有自己寫,編譯器會(huì)自己實(shí)現(xiàn)一個(gè)默認(rèn)的賦值運(yùn)算符重載,在運(yùn)行是是以值得方式逐字節(jié)拷貝。 上面得拷貝構(gòu)造函數(shù)中,編譯器自己默認(rèn)創(chuàng)建的拷貝構(gòu)造函數(shù)也是相同的,只能進(jìn)行淺拷貝,只能拷貝值無法為其分配內(nèi)存,但賦值運(yùn)算符重載還是有一點(diǎn)不同的,它初始化需要分配空間的時(shí)候,會(huì)先為創(chuàng)建的對象分配空間,之后在使用賦值運(yùn)算符,將分配好的空間舍棄,存入其他對象的空間地址。

如下代碼:

// 這里會(huì)發(fā)現(xiàn)下面的程序會(huì)崩潰掉?這里就需要我們以后講的深拷貝去解決。
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(capacity * sizeof(int));
		if (nullptr == _array)
		{
			perror("malloc申請空間失敗");
			return;
		}
			_size = 0;
		_capacity = capacity;
	}
	void Push(const int& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2;
	s2 = s1;
	return 0;
}

我們要注意,如果類中未涉及到資源管理,賦值運(yùn)算符是否實(shí)現(xiàn)都可以;一旦涉及到資源管理則必須要實(shí)現(xiàn)。

5.3前置++和后置++重載

對于前置++,我們按照正常的運(yùn)算符重載模式寫即可,但記得返回類型需要使用類類型&,將修改后的對象返回。

class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator++()
	{
		_year += 1;
		return *this;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date today(2023, 2, 7);
	Date d;
	d = ++today; //d:2024.2,7  today:2024,2,7

	return 0;
}

至于后置++,為了可以讓兩個(gè)函數(shù)實(shí)現(xiàn)重載,規(guī)定增加一個(gè)int類型的參數(shù),作為區(qū)分。

注意:前置++是先++后使用,所以可以直接返回其修改后的對象,對于后置++是先使用后++,所以返回的應(yīng)該是未修改的對象,我們可以在修改原對象前對其進(jìn)行拷貝,然后修改原對象,返回時(shí)直接返回之前拷貝的對象,這樣原對象即改變了,使用的也是未改變的對象,符合后置++

class Date
{
public:

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date operator++()
	{
		Date& temp(*this);
		_year += 1;
		return temp;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date today(2023, 2, 7);
	Date d;
	d = today++; //d:2023,2,7  today:2024,2,7

	return 0;
}

5.4流插入和流提取運(yùn)算符重載

在C++中,我們輸出和輸入一個(gè)數(shù)據(jù)通常是通過cout、cin,它們兩其實(shí)就是一個(gè)類對象,重載了<<、>>兩個(gè)運(yùn)算符,所以輸入、輸出其實(shí)就是調(diào)用兩個(gè)運(yùn)算符重載函數(shù)。

如上圖,它們的類型分別為ostream、istream,存放在iostream這個(gè)頭文件中,而C++庫內(nèi)定義的東西都存放在std這個(gè)命名空間內(nèi),所以我們每次開頭需要寫這兩行代碼。

對于內(nèi)置類型,如下:

int a = 10;
double b = 10.0;
cout << a;
cout << b;

通過函數(shù)重載調(diào)用不同的運(yùn)算符函數(shù),將其打印。

下面我們一起來看一下這兩個(gè)運(yùn)算符是如何重載的。

流提取

在類中定義:

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void operator<<(ostream& out)
	{
        //下面就是輸出內(nèi)置類型的值,流提取調(diào)用頭文件<iostream>內(nèi)的
		out << _year << "年" << _month << "月" << _day << "日" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date today;
    //第一個(gè)參數(shù)為左操作數(shù),第二個(gè)參數(shù)為右操作數(shù),由創(chuàng)建的對象調(diào)用類內(nèi)的重載函數(shù)
    //today.operator<<(cout)
	today << cout;

	return 0;
}

我們看到,函數(shù)的使用形式是today << cout;,類對象搶占第一個(gè)參數(shù),一定在左邊,cout在右邊,這么寫肯定不符合我們平常的習(xí)慣,如果要將cout放在第一個(gè)位置,我們需要將函數(shù)在全局定義。

class Date
{
public:
	friend ostream& operator<<(ostream& out, const Date& d);
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

//不對對象的成員變量做修改,最好使用const修飾,防止寫錯(cuò),發(fā)生錯(cuò)誤
ostream& operator<<(ostream& out,const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
    return out;
}

int main()
{
	Date today;
	cout << today;

	return 0;
}

如上面的代碼,我們函數(shù)變?yōu)槿趾?,很好的解決了位置的問題,但我們又無法訪問類中的成員變量,這里有三種方法,我們使用第一種

使該函數(shù)變?yōu)轭惖挠言瘮?shù),在類中public作用域下,使用friend修飾函數(shù)的聲明,即可在該函數(shù)內(nèi)使用對應(yīng)類的對象調(diào)用成員變量。增加接口,在類中創(chuàng)建輸出函數(shù),調(diào)用對應(yīng)函數(shù)即可得到對應(yīng)的成員變量值,對象在類外無法訪問成員變量,但可以訪問對外開發(fā)的函數(shù)。(java喜歡這么做)刪除private作用域,這樣成員變量就可以訪問。(不建議這么做,破壞封裝性)

為了防止出現(xiàn)下面的情況,以此要輸出多個(gè)對象的值,我們需要使重載的函數(shù)返回cout,使函數(shù)可以正常運(yùn)行。

cout << d1 << d2 << d3 << endl;
//cout << d1  //調(diào)用重載函數(shù),調(diào)用后返回cout繼續(xù)執(zhí)行
//cout << d2  //同時(shí),運(yùn)行后返回cout
//..
//cout << endl; //與重載的類型不匹配,調(diào)用頭文件內(nèi)的函數(shù)

流插入

class Date
{
public:
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

//需要改變對象的成員變量,不能使用const修飾
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

int main()
{
	Date today;

	cin >> today;
	cout << today;

	return 0;
}

如上面的代碼與流提取相似。

6.const成員

如下面的代碼,是否可以正常運(yùn)行呢?

class Date
{
public:
	Date(int year=2000,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	const Date d;
	d.Print();

	return 0;
}

它不能正常運(yùn)行,因?yàn)閷ο骴使用const修飾了,它的值是無法改的(該對象的成員變量無法修改)。在調(diào)用成員函數(shù)時(shí),編譯器默認(rèn)傳過去的值為Date* const this,this指針表示對象本身,意味著在此函數(shù)內(nèi)成員變量可以改變,這產(chǎn)生了沖突。更簡單的說,這就是將原本只能讀的對象變成可讀可寫,無視其權(quán)限。

想要解決這個(gè)問題,只要使用const修飾*this使其無法改變即可,而this又是編譯器默認(rèn)的,它是被隱藏著的不好修改,C++給出了如下方法,在成員函數(shù)的括號(hào)后直接加const即表示修飾*this,如下

	void Print() const
	{
		cout << "Print" << endl;
	}

如果我們使用為被修飾的const對象調(diào)用被const修飾的成員函數(shù),這時(shí)可以的,原本對象就可以通過調(diào)用成員函數(shù)修改和讀取,現(xiàn)在只是傳過去只能使成員函數(shù)讀取這沒有問題。

class Date
{
public:
	Date(int year=2000,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const
	{
		cout << "Print" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	d.Print();

	return 0;
}

同理:在類中成員函數(shù)是可以相互調(diào)用的,但被const修飾的成員函數(shù)無法調(diào)用沒有被修飾的,因?yàn)楸恍揎椀某蓡T函數(shù)所函數(shù)*this指針是無法改變的,而沒有被修飾的是可以改變的,const失去了作用,這種寫法是錯(cuò)誤的。而沒有被修飾的成員函數(shù)是可以調(diào)用被修飾的,這屬于即可讀又可寫的情況向只可讀的情況發(fā)展,沒有改變語法。

注意:

類內(nèi)部不改變成員變量的成員函數(shù),最好加上const,防止數(shù)據(jù)被修改

一般會(huì)在下面的場景用到const成員

class Date
{
public:
	Date(int year=2000,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const
	{
		cout << _year << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void test(const Date& d)
{
	d.Print();
}

int main()
{
	Date td;
	test(td);

	return 0;
}

我們在創(chuàng)建對象之初一般不為其修飾cosnt,但我們會(huì)經(jīng)常將對象作為實(shí)參傳遞給其他函數(shù),如果形參被const修飾,那在這個(gè)函數(shù)內(nèi)它只能被讀,無法修改意味著調(diào)用的成員函數(shù)也必須被const修飾。

const這種寫法只針對成員函數(shù)

若定義和聲明分離,需要修飾const時(shí),定義和聲明都要修飾

成員函數(shù)被const修飾和不被修飾構(gòu)成const重載

	void Print() const
	{
		cout << _year << endl;
	}
	void Print()
	{
		cout << _year << endl;
	}

一個(gè)形參為Date* const this,一個(gè)為const Date* const this,形參不同滿足重載

若是成員函數(shù)被const修飾注意它的返回值類型,若返回的是成員變量,也需要修飾const,否則權(quán)限發(fā)生變化,編譯會(huì)出錯(cuò)

7.取地址重載和const取地址操作符重載

取地址重載和const取地址操作符重載是最后兩個(gè)編譯器默認(rèn)生成的成員函數(shù),我們一般不會(huì)去寫它,而是直接去使用編譯器默認(rèn)生成的。

class Date
{
public:
	Date(int year=2000,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	const Date d2;
	cout << &d1 << endl;
	cout << &d2 << endl;

	return 0;
}

我們?nèi)绻胍獙懗鰜硪部梢裕缦拢?/p>

class Date
{
public:
	Date(int year=2000,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date* operator&()//取地址重載
	{
		return this;
	}
	const Date* operator&() const //const取地址操作符重載
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	const Date d2;
	cout << &d1 << endl;
	cout << &d2 << endl;

	return 0;
}

兩個(gè)使用的場景不同,取地址重載用在取一般的對象的地址,const取地址操作符重載用在取被const修飾的對象的地址。

總結(jié)

到此這篇關(guān)于C++類和對象之類的6個(gè)默認(rèn)成員函數(shù)的文章就介紹到這了,更多相關(guān)C++類的默認(rèn)成員函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++ lambda 捕獲模式與右值引用的使用

    C++ lambda 捕獲模式與右值引用的使用

    這篇文章主要介紹了C++ lambda 捕獲模式與右值引用的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 一篇文章帶你了解C語言內(nèi)存對齊公式

    一篇文章帶你了解C語言內(nèi)存對齊公式

    這篇文章主要介紹了C語言內(nèi)存對齊,包括內(nèi)存對其的基本概念及用法,以及注意事項(xiàng),并以實(shí)例形式加以說明,需要的朋友可以參考下,希望能給你帶來幫助
    2021-08-08
  • C語言編程const遇上指針分析

    C語言編程const遇上指針分析

    本篇文章是C語言編程篇,主要為大家介紹C語言編程中當(dāng)Const遇上指針的分析講解,有需要的朋友可以借鑒參考下,希望可以有所幫助
    2021-09-09
  • C/C++獲取鍵盤事件的方法

    C/C++獲取鍵盤事件的方法

    今天小編就為大家分享一篇C/C++獲取鍵盤事件的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • C語言實(shí)現(xiàn)UDP通信

    C語言實(shí)現(xiàn)UDP通信

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)UDP通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 初識(shí)C++?Vector模板與實(shí)例化原理

    初識(shí)C++?Vector模板與實(shí)例化原理

    這篇文章主要為大家介紹了初識(shí)C++?Vector模板與實(shí)例化原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • C++類型轉(zhuǎn)換運(yùn)算符的實(shí)例詳解

    C++類型轉(zhuǎn)換運(yùn)算符的實(shí)例詳解

    這篇文章主要介紹了C++類型轉(zhuǎn)換運(yùn)算符的實(shí)例詳解的相關(guān)資料,希望通過本文大家能夠掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • c++遞歸實(shí)現(xiàn)n皇后問題代碼(八皇后問題)

    c++遞歸實(shí)現(xiàn)n皇后問題代碼(八皇后問題)

    c++遞歸實(shí)現(xiàn)n皇后問題代碼分享,大家參考使用吧
    2013-12-12
  • 基于樹莓派實(shí)現(xiàn)播放MP3音樂

    基于樹莓派實(shí)現(xiàn)播放MP3音樂

    這篇文章主要為大家詳細(xì)介紹了基于樹莓派實(shí)現(xiàn)播放MP3音樂,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • 基于c++的中國象棋游戲設(shè)計(jì)與實(shí)現(xiàn)

    基于c++的中國象棋游戲設(shè)計(jì)與實(shí)現(xiàn)

    這篇文章主要介紹了基于c++的中國象棋游戲設(shè)計(jì)與實(shí)現(xiàn),主要操作是possibleMove(int?x,?int?y),通過整個(gè)棋盤每個(gè)位置上的信息、中國象棋的規(guī)則來獲得位置(x,?y)這個(gè)棋子可以移動(dòng)到的位置,需要的朋友可以參考一下
    2022-02-02

最新評論