最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++面向?qū)ο笾畬ο蟮馁x值詳解

 更新時間:2026年05月08日 15:10:29   作者:sparEE  
本文講解了C++中對象初始化、復(fù)制構(gòu)造函數(shù)及賦值操作的實現(xiàn)與優(yōu)化,通過Stack類的實例,展示了默認(rèn)賦值帶來的內(nèi)存泄漏問題,以及通過重載賦值運算符解決該問題的過程,最后強(qiáng)調(diào)了使用explicit關(guān)鍵字以限制隱式構(gòu)造函數(shù)調(diào)用的重要性

對象初始化:構(gòu)造函數(shù)和復(fù)制構(gòu)造函數(shù)

在設(shè)計一個類時,往往要設(shè)計構(gòu)造函數(shù)。一般對象的初始化使用構(gòu)造函數(shù)初始化,如果沒有構(gòu)造函數(shù)則會使用默認(rèn)構(gòu)造函數(shù)。還可以用復(fù)制構(gòu)造函數(shù)來通過一個已有對象初始化一個新的對象。設(shè)計一個類來表現(xiàn)對象的初始化,復(fù)制構(gòu)造,以及賦值。

在Stack.hpp頭文件中聲明類的成員。

//Stack.hpp
#pragma once
class Stack {
public:
	Stack(int max_size);
	~Stack();
	//復(fù)制構(gòu)造函數(shù)
	Stack(const Stack& s);
	bool IsEmpty() const;
	bool IsFull() const;
	void Push(int data);
	void Pop();
	int Top() const;
private:
	int* buffer_;
	int* top_;
	int capacity_;
};

在Stack.cpp中實現(xiàn)類中的方法。包括有一個表示大小的參數(shù)的構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),???,棧滿的判斷函數(shù),以及Push、Pop、Top等方法。

//Stack.cpp
#include "Stack.hpp"
#include <iostream>
Stack::Stack(int max_size) :
	capacity_(max_size), 
	//buffer_(new int[max_size]),
	buffer_(static_cast<int*>(std::malloc(sizeof(int) * max_size))), 
	top_(buffer_) {}
Stack::~Stack() {
	std::free(buffer_);
}
Stack::Stack(const Stack& s) :
	capacity_(s.capacity_),
	buffer_(static_cast<int*>(std::malloc(sizeof(int) * s.capacity_))),
	top_(buffer_ - s.buffer_ + s.top_) {
	std::memcpy(buffer_, s.buffer_, sizeof(int) * s.capacity_);
}
bool Stack::IsEmpty() const {
	return top_ <= buffer_;
}
bool Stack::IsFull() const {
	return top_ >= buffer_ + capacity_;
}
void Stack::Push(int data) {
	if (this->IsFull())
		throw "Stack is full!";
	*top_++ = data;
}
void Stack::Pop() {
	if (this->IsEmpty())
		throw "Stack is Empty!";
	top_--;
}
int Stack::Top() const {
	if (this->IsEmpty())
		throw "Stack is Empty!";
	return *(top_ - 1);
}

在主函數(shù)前引用Stack.hpp頭文件。創(chuàng)建一個s1對象并push兩個值,在使用拷貝構(gòu)造復(fù)制s1創(chuàng)建對象s2。確認(rèn)s1和s2的棧頂相等。在s2中push一個新的值,s1和s2的棧頂變得不相等。

創(chuàng)建新的對象s3,再使用類默認(rèn)的賦值運算符重載給s3賦值為s1。這樣的賦值會把棧中的指針屬性的地址直接復(fù)制到新的對象。在給 s3 push 了新的值之后,s1、s3的 top_ 屬性變得不相等,但是s1、s3的buffer卻指向同一區(qū)域,在程序結(jié)束調(diào)用析構(gòu)時會重復(fù)釋放同一塊內(nèi)存造成報錯。

//OverloadAssignment
#include <iostream>
#include "Stack.hpp"
int main() {
	Stack s1(16);
	s1.Push(1);
	s1.Push(2);
	Stack s2 = s1;	//調(diào)用復(fù)制構(gòu)造函數(shù)
	std::cout << "s1 top: " << s1.Top() << std::endl;
	std::cout << "s2 top: " << s2.Top() << std::endl;
	s2.Push(3);
	std::cout << "s2 pushed 3" << std::endl;
	std::cout << "s1 top: " << s1.Top() << std::endl;
	std::cout << "s2 top: " << s2.Top() << std::endl;
	Stack s3(16);
	s3 = s1;	//調(diào)用賦值運算符重載
	std::cout << "s3 top: " << s3.Top() << std::endl;
	//觀察報錯信息,s3的析構(gòu)函數(shù)被調(diào)用了兩次,說明s3和s1指向了同一塊內(nèi)存區(qū)域,導(dǎo)致內(nèi)存被重復(fù)釋放了。
}

雖然能夠正常輸出所有結(jié)果,但是在程序結(jié)束后析構(gòu)對象時由于重復(fù)釋放相同空間引發(fā)報錯。

s1 top: 2
s2 top: 2
s2 pushed 3
s1 top: 2
s2 top: 3
s1 top: 2
s3 top: 2
s3 pushed 3
s1 top: 2
s3 top: 3

針對報錯,copilot的分析:

簡短結(jié)論(錯誤) 程序在 std::free(buffer_) 處觸發(fā)斷點/崩潰。根本原因是對象復(fù)制后發(fā)生了淺拷貝——s3 = s1; 使用了編譯器生成的默認(rèn)賦值運算符(類聲明只有拷貝構(gòu)造函數(shù),未定義 operator=),導(dǎo)致 s1 和 s3 共享同一 buffer_ 指針,程序結(jié)束時兩次釋放同一內(nèi)存(double free/堆破壞),因此 free 拋出異常/斷點。


為什么會發(fā)生(<200字) Stack 定義了自定義拷貝構(gòu)造函數(shù)(會分配新緩沖區(qū)),但沒有自定義拷貝賦值運算符,編譯器生成的默認(rèn) operator= 做逐成員賦值,把 buffer_ 指針從 s1 拷貝到 s3(淺拷貝)。當(dāng)兩個對象析構(gòu)時會重復(fù)釋放同一塊內(nèi)存或在賦值后原對象的緩沖區(qū)被提前釋放或覆蓋,導(dǎo)致堆損壞,free 崩潰。

重載賦值運算符

如果需要對自定義對象賦值,就需要重載賦值運算符。

在Stack.hpp中聲明重載賦值運算符的函數(shù)。并在Stack.cpp中完成。

//重載賦值運算符
//函數(shù)的返回值是一個引用,指向當(dāng)前對象。在s3 = s2 = s1;這樣的鏈?zhǔn)劫x值中,s2 = s1的結(jié)果是s2的引用,然后s3 = s2實際上是s3 = (s2 = s1),因此s3最終也會得到s1的值。
Stack& Stack::operator =(const Stack& s) {
	//給自己賦值
	if (this == &s)
		return *this;
	//釋放原有資源
	std::free(buffer_);
	capacity_ = s.capacity_;
	buffer_ = static_cast<int*>(std::malloc(sizeof(int) * s.capacity_));
	top_ = buffer_ - s.buffer_ + s.top_;
	std::memcpy(buffer_, s.buffer_, sizeof(int) * s.capacity_);
	return *this;
}

賦值函數(shù)的返回值是一個引用,指向當(dāng)前對象。在s3 = s2 = s1;這樣的鏈?zhǔn)劫x值中,s2 = s1的結(jié)果是s2的引用,然后s3 = s2實際上是s3 = (s2 = s1),因此s3最終也會得到s1的值。

再去運行之前的主函數(shù),就不會發(fā)生重復(fù)釋放內(nèi)存的報錯了。

這樣的賦值函數(shù)參數(shù)是自己的類型,如果不手動實現(xiàn)會隱含一個默認(rèn)的方法。這樣的賦值函數(shù)叫做拷貝賦值函數(shù)。與拷貝構(gòu)造函數(shù)相似,都是傳遞一個自身的const引用類型。

除了拷貝賦值函數(shù)也存在一般的賦值函數(shù),以一個復(fù)數(shù)類為例:

class Complex {
public:
	Complex(double r, double i) : real_(r), imag_(i) {};
	Complex& operator =(double r) {
		real_ = r;
		imag_ = 0.0;
		return *this;
	}
private:
	double real_, imag_;
};

隱式構(gòu)造

除此之外,如果類中沒有聲明賦值函數(shù),但是存在有一個參數(shù)的構(gòu)造函數(shù),也可以用隱式構(gòu)造的方法使用等號給對象賦值。

class Complex {
public:
	Complex(double r) : real_(r), imag_(0.0) {};
	Complex(double r, double i) : real_(r), imag_(i) {};
public:
	double real_, imag_;
};
int main() {
	Complex c1(1, 2);
	//使用了隱式構(gòu)造函數(shù)
	Complex c2 = 45.0;
	//使用了隱式構(gòu)造函數(shù)
	c1 = 4;
	std::cout << c1.real_ << " + " << c1.imag_ << "i" << std::endl;
	std::cout << c2.real_ << " + " << c2.imag_ << "i" << std::endl;
	return 0;
}

如果不希望使用隱式構(gòu)造,可以在構(gòu)造函數(shù)前加explicit關(guān)鍵字,explicit翻譯是明確的、不加掩飾的,計算機(jī)中是顯式的意思,加上explicit的構(gòu)造函數(shù)無法被隱式調(diào)用。

比如在Stack類中構(gòu)造函數(shù)可以被隱式調(diào)用,但是這種用法意義不明確,如果不希望實現(xiàn)這種用法可以在構(gòu)造函數(shù)前加上explicit。

class Stack {
public:
	explicit Stack(int max_size);
//……
int main (){
    Stack st(16);
    st = 20;    //會報錯
}

比如在復(fù)數(shù)類中同時定義了另一個構(gòu)造函數(shù),此時使用隱式構(gòu)造會產(chǎn)生歧義導(dǎo)致程序無法運行,可以把這個有兩個參數(shù)且有初值的構(gòu)造函數(shù)指定為explicit。

class Complex {
public:
	Complex(double r) : real_(r), imag_(0.0) {};
	explicit Complex(double r = 0, double i = 0) : real_(r), imag_(i) {};
public:
	double real_, imag_;
};
int main() {
	Complex c1(1, 2);
	//使用了隱式構(gòu)造函數(shù)
	Complex c2 = 45.0;
	//使用了隱式構(gòu)造函數(shù)
	c1 = 4;
	std::cout << c1.real_ << " + " << c1.imag_ << "i" << std::endl;
	std::cout << c2.real_ << " + " << c2.imag_ << "i" << std::endl;
	return 0;
}

這樣可能沒什么意義,因為給了初值的構(gòu)造函數(shù)可以用單個參數(shù)隱式構(gòu)造也可以用多個參數(shù)隱式構(gòu)造。

class Complex {
public:
    Complex(double r = 0, double i = 0) : real_(r), imag_(i) {};
public:
	double real_, imag_;
};
int main() {
	Complex c1(1, 2);
	//使用了隱式構(gòu)造函數(shù)
	Complex c2 = 45.0;
	//使用了隱式構(gòu)造函數(shù)
	c1 = 4;
	std::cout << c1.real_ << " + " << c1.imag_ << "i" << std::endl;
	std::cout << c2.real_ << " + " << c2.imag_ << "i" << std::endl;
	//使用多個參數(shù)的隱式構(gòu)造
	c1 = { 45, 56 };
	std::cout << c1.real_ << " + " << c1.imag_ << "i" << std::endl;
	return 0;
}

到此這篇關(guān)于c++面向?qū)ο螅簩ο蟮馁x值的文章就介紹到這了,更多相關(guān)c++對象的賦值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++如何分割字符串示例代碼

    c++如何分割字符串示例代碼

    因為c++字符串沒有split函數(shù),所以字符串分割單詞的時候必須自己手寫,也相當(dāng)于自己實現(xiàn)一個split函數(shù)吧!下面跟小編一起來看看如何實現(xiàn)這個功能。
    2016-08-08
  • Linux UDP服務(wù)端和客戶端程序的實現(xiàn)

    Linux UDP服務(wù)端和客戶端程序的實現(xiàn)

    這篇文章主要介紹了Linux UDP服務(wù)端和客戶端程序的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • C++內(nèi)存管理介紹

    C++內(nèi)存管理介紹

    這篇文章主要介紹了C++內(nèi)存管理,C++標(biāo)準(zhǔn)委員會給我們提供了auto_ptr智能指針,后面又引入了share_ptr以及weak_ptr幫助我們正確和安全的使用指針,本文主要是介紹boost庫提供的解決方案,需要的朋友可以參考一下
    2022-01-01
  • C++入門基礎(chǔ)之命名空間、輸入輸出和缺省參數(shù)

    C++入門基礎(chǔ)之命名空間、輸入輸出和缺省參數(shù)

    C++入門基礎(chǔ)篇的內(nèi)容為C++的基本特性,只有在掌握C++的基本特性后,是進(jìn)入后面類和對象學(xué)習(xí)的基礎(chǔ),下面這篇文章主要給大家介紹了關(guān)于C++入門基礎(chǔ)之命名空間、輸入輸出和缺省參數(shù)的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • C++ 中const修飾虛函數(shù)實例詳解

    C++ 中const修飾虛函數(shù)實例詳解

    這篇文章主要介紹了C++ 中const修飾虛函數(shù)實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C語言基于圖形庫實現(xiàn)雙人貪吃蛇

    C語言基于圖形庫實現(xiàn)雙人貪吃蛇

    這篇文章主要為大家詳細(xì)介紹了C語言基于圖形庫實現(xiàn)雙人貪吃蛇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • c++ bitset詳解

    c++ bitset詳解

    這篇文章主要介紹了C++ bitset用法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • C語言數(shù)組按協(xié)議存儲與按協(xié)議解析數(shù)據(jù)的實現(xiàn)

    C語言數(shù)組按協(xié)議存儲與按協(xié)議解析數(shù)據(jù)的實現(xiàn)

    今天小編就為大家分享一篇關(guān)于C語言數(shù)組按協(xié)議存儲與按協(xié)議解析數(shù)據(jù)的實現(xiàn),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C++中的Lambda表達(dá)式及表達(dá)式語句

    C++中的Lambda表達(dá)式及表達(dá)式語句

    這篇文章主要介紹了C++中的Lambda表達(dá)式及表達(dá)式語句,表達(dá)式這個概念在C++中屬于比較細(xì)節(jié)的知識了,很多時候我們只用知道怎么用,對于編譯器內(nèi)部怎么處理我們并不關(guān)心;并且關(guān)于左值和右值這個概念,也是C++比較深的一個小知識點,需要的朋友可以參考一下
    2021-12-12
  • C++11 智能指針的具體使用

    C++11 智能指針的具體使用

    本文主要介紹了C++11 智能指針的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評論

富裕县| 永仁县| 洪洞县| 喀什市| 滦南县| 苍南县| 龙岩市| 禹城市| 白水县| 大化| 巍山| 淳化县| 潞城市| 新宾| 白河县| 裕民县| 若尔盖县| 绥德县| 班戈县| 视频| 阿瓦提县| 乌兰浩特市| 措勤县| 齐河县| 泾川县| 西华县| 东乌珠穆沁旗| 曲周县| 兴山县| 达日县| 岑溪市| 大悟县| 花莲县| 安远县| 镇雄县| 广昌县| 元朗区| 高安市| 湟源县| 建平县| 宁陵县|