1. 程式人生 > >C++封裝篇基礎知識

C++封裝篇基礎知識

類的定義方式:

// 以下包含的東西會匯入到應用這個檔案的檔案
#include <string>
#include <iostream>
using namespace std;

/**
 * .h檔案 實現宣告成員變數和成員函式作用
 */
class Liu {
public:
	Liu(); // 無參構造( 預設) : 不用傳引數的建構函式都是預設建構函式,所以有些建構函式所有引數都被初始化時,則會產生衝突,編譯不過(初始化列表)
//	Liu(string _strName = "liuguangxi00"); //等同於上面的Liu();
	Liu(string _strName); //有參構造
	Liu(string _strName,int age); //有參構造
//	Liu(string _strName = "liugx", int age = 26, int money = 3000000); //初始化列表構造
//	Liu(const Liu& gx);// 拷貝建構函式

	void setName(string _strName);
	string getName();
	int getMoney();
	int getWife();
private:
	string m_strName;
	int mAge;
	const int mWife = 10;
	const int mMoney = 50000;
};

#include "Liu.h"

/**
 * C++不同檔案類外定義程式碼演示
 */

Liu::Liu() {
	Liu::m_strName = "liuguangxi";
	Liu::setName("liugx");
	cout << "Liu()" << endl;
}

Liu::Liu(string _strName) {
	Liu::m_strName = _strName;
	cout << "Liu(string _strName)" << endl;
}

Liu::Liu(string _strName, int age) {
	Liu::m_strName = _strName;
	Liu::mAge = age;
	cout << m_strName << "_" << mAge << endl;
}

////初始化列表的應用
//Liu::Liu(string _strName, int age, int money) :
//		m_strName(_strName), mAge(age), mMoney(money) {
//	cout << m_strName << "_" << mAge << "_" << mMoney << endl;
//}

//Liu::Liu(const Liu& gx):m_strName(_strName), mAge(age),mMoney(money){
//	cout << "Liu拷貝建構函式被執行!!!" << endl;
//}

void Liu::setName(string _strName) {
	Liu::m_strName = _strName;
}

string Liu::getName() {
	return Liu::m_strName;
}

int Liu::getMoney() {
	return mMoney;
}

int Liu::getWife() {
	return mWife;
}

#include <iostream>
#include <string>
#include "Liu.h"
using namespace std;

/**	一、	物件是如何儲存在記憶體中的?
 * 			記憶體分割槽
 * 1、棧區 	int i = 1;定義一個變數
 * 2、堆區	int *p = new int[10];
 * 3、全域性區	儲存的全域性變數和靜態變數
 * 4、常量區	string str = "c++"
 * 5、程式碼區	儲存邏輯程式碼的二進位制
 */

/**
 * 二、 C++建構函式的初始化規則和特性與java完全相同
 */

/**
 * 三 、與java不同的是,C++建構函式存在初始化列表
 */

int main() {
	Liu liu;
	cout<< liu.getName() << endl;
	Liu liu0("liugx00");
	cout<< liu0.getName() << endl;
	return 0;
}

建構函式初始化列表:

// 以下包含的東西會匯入到應用這個檔案的檔案
#include <string>
#include <iostream>
using namespace std;

/**
 *
 */
class Liu {
public:
	Liu(string _strName = "liugx", int age = 26, int money = 3000000); //初始化列表構造
private:
	string m_strName;
	int mAge;
//	const int mWife(10);
	const int mWife = 10; // 已賦值,不能用建構函式初始化列表方式進行重新賦值
	const int mMoney;
};

#include "Liu.h"

//方式一、初始化列表的使用方式
Liu::Liu(string _strName, int age, int money) :
		m_strName(_strName), mAge(age), mMoney(money) {
	cout << m_strName << "_" << mAge << "_" << mMoney << "_" << mWife<< endl;
}

//方式二、普通方式進行屬性的初始化
//Liu::Liu(string _strName, int age, int money) {
//	m_strName = _strName;
//	mAge = age;
//	mMoney = money; //普通初始化與初始化列表區別就在於此行程式碼不通過編譯器,因為初始化列表的賦值是在建構函式的執行之前賦值的
//	cout << m_strName << "_" << mAge << "_" << mMoney << "_" << mWife<< endl;
//}

#include <iostream>
#include "Liu.h"
using namespace std;

/**
 * 初始化列表所有引數都必須要被初始化,建構函式初始化列表就是預設建構函式,即無參建構函式之一(如果兩者同時存在,編譯時編譯器無法分辨)
 *
 * 建構函式初始化列表與函式特性章節所講內容類似,可供參考,與普通函式採用函式引數預設值進行賦值時最大的區別就是,
 * 採用普通函式預設引數值進行賦值時不能賦值給const修飾過的常量,
 * 而採用初始化列表則可以賦值給const修飾過的常量(在常量還未賦值之前進行初始化列表賦值)
 *
 * 初始化列表特性
 * 1、初始化列表執行先於建構函式
 * 2、初始化列表只能用於建構函式
 *
 * 初始化列表是寫在函式右邊的。示例程式碼:Liu::Liu(string _strName, int age, int money) :
		m_strName(_strName), mAge(age), mMoney(money){}
 */
int main() {
	Liu liu;
	Liu liugx("liuguangxi",27);
	return 0;
}

拷貝建構函式:

// 以下包含的東西會匯入到應用這個檔案的檔案
#include <string>
#include <iostream>
using namespace std;

/**
 * 遺留問題點:若建構函式初始化列表去初始化的引數中有被const修飾過的引數,則拷貝建構函式不能通過編譯
 */
class Liu {
public:
//	Liu();
	Liu(string _strName = "liugx", int age = 26, int money = 3000000); //初始化列表構造
	// 拷貝建構函式定義格式:類名(const 類名& 變數名)
	Liu(const Liu&liu); // 拷貝建構函式

private:
	string m_strName;
	int mAge;
	const int mWife = 10;
	int mMoney;
//	 const int mMoney = 10; //如果加上const,拷貝建構函式既然報錯???
	/**
	 * 複寫了拷貝建構函式  即是這個類是被const修飾過?
	 */
};

#include "Liu.h"

//初始化列表的應用
Liu::Liu(string _strName, int age, int money) :
		m_strName(_strName), mAge(age), mMoney(money) {
	cout << m_strName << "_" << mAge << "_" << mMoney << endl;
}

//Liu::Liu() {
//	cout << "Liu()" << endl;
//}


Liu::Liu(const Liu&liu){
	cout << "Liu拷貝建構函式被執行!!!" << endl;
}

#include <iostream>
#include <string>
#include "Liu.h"
using namespace std;

void test(Liu liu){}

int main() {
	Liu liu;
	Liu liug = liu;
	Liu liugx(liu);
	// 以上理論上是執行三次Liu的建構函式,但事實上是隻執行了一次,因此出現了拷貝建構函式
	// 拷貝建構函式定義格式:類名(const 類名 &變數名)
	// 如果沒有定義拷貝建構函式,則系統自動生成一個預設的拷貝建構函式
	// 當採用直接初始化或者複製初始化例項物件時系統會自動呼叫拷貝建構函式
	test(liu);
	return 0;
}

解構函式:

#include <string>
#include <iostream>
using namespace std;

class Teacher {
public:
	Teacher(); // 無參構造( 預設)
	Teacher(string _strName); //有參構造
	Teacher(const Teacher&tea);// 拷貝建構函式
	~Teacher(); //析構構造(物件被銷燬時回撥)
	void setName(string _strName);
	// 定義資料成員封裝函式getName()
	string getName();
	//定義Student類私有資料成員m_strName
private:
	string m_strName;
};

#include "Teacher.h"

Teacher::Teacher() {
	Teacher::m_strName = "luke";
	cout << "Teacher()" << endl;
}

Teacher::Teacher(string _strName) {
	Teacher::m_strName = _strName;
	cout << "Teacher(string _strName)" << endl;
}

Teacher::Teacher(const Teacher&tea){
	cout << "拷貝建構函式被執行!!!" << endl;
}

Teacher::~Teacher() {
	cout << "~Teacher()" << endl;
}

// 定義資料成員封裝函式setName()
void Teacher::setName(string _strName) {
	Teacher::m_strName = _strName;
}

// 定義資料成員封裝函式getName()
string Teacher::getName() {
	return Teacher::m_strName;
}

#include <iostream>
#include "Teacher.h"
using namespace std;

/**
 * 1、解構函式如果沒有自定義,則系統自動生成
 *
 * 2、解構函式在物件銷燬時自動呼叫,類似於Android中activity的ondestroy()
 *
 * 3、解構函式於建構函式一樣沒有返回值,但是沒有引數,即是不能過載該函式
 */
int main() {
	Teacher tea;
	Teacher t = tea;
	return 0;
}

// 以下包含的東西會匯入到應用這個檔案的檔案
#include <string>
#include <iostream>
using namespace std;

/**
 * .h檔案 實現宣告成員變數和成員函式作用
 */
class Liu {
public:
//	Liu(); // 無參構造( 預設)
//	Liu(string _strName); //有參構造
	Liu(string _strName = "liugx", int age = 26, int money = 3000000); //初始化列表構造
//	Liu(const Liu& gx);// 拷貝建構函式

	void setName(string _strName);
	string getName();
	int getMoney();
	int getWife();
private:
	string m_strName;
	int mAge;
	const int mWife = 10;
	const int mMoney;//若要改變常量,必須要在初始化列表中進行初始化時才能改變
};

#include "Liu.h"

/**
 * C++不同檔案類外定義程式碼演示
 */
Liu::Liu(string _strName, int age, int money) :
		m_strName(_strName), mAge(age), mMoney(money) {
	cout << m_strName << "_" << mAge << endl;
}

void Liu::setName(string _strName) {
	Liu::m_strName = _strName;
}

string Liu::getName() {
	return Liu::m_strName;
}

int Liu::getMoney() {
	return mMoney;
}

int Liu::getWife() {
	return mWife;
}

#include <iostream>
using namespace std;

/**
 * 定義類:Student
 * 資料成員:m_strName
 * 資料成員的封裝函式:setName()、getName()
 */
class Student {
private:
	string m_strName;//定義Student類私有資料成員m_strName
public:
	int mAge;
	// 定義資料成員封裝函式setName()
	void setName(string _strName) {
		m_strName = _strName;
	}

	// 定義資料成員封裝函式getName()
	string getName() {
		return m_strName;
	}

};

int main() {
	Student std;
	std.mAge = 26;
	cout << std.mAge << endl;
	// 使用new關鍵字,例項化物件
	Student *str = new Student();
	// 設定物件的資料成員
	str->setName("深圳人");
	// 使用cout列印物件str的資料成員
	cout << str->getName() << endl;
	// 將物件str的記憶體釋放,並將其置空
	delete str;
	str = NULL;
	return 0;
}

物件指標:

#include <iostream>
using namespace std;

class Coordinate {
public:
	int mX;
	int mY;
};

int main() {
	/**
	 * 堆方式指標
	 */
	// 寫法方式一
	Coordinate *p = new Coordinate();
	if (NULL == p) {
		return 0;
	}
	p->mX = 10; //(*p).mX = 10;
	p->mY = 20; //(*p).mY = 20;

	cout << (*p).mX << endl;
	cout << (*p).mY << endl;
	delete p;
	p = NULL;
// 寫法方式二
	Coordinate *p1 = NULL;
	p1 = new Coordinate;
	(*p1).mX = 10;
	delete p1;
	p1 = NULL;

	/**
	 * 棧方式指標
	 */
	Coordinate p2;
	Coordinate *p3 = &p2;
	p3->mX = 10;
	p3->mY = 18;
	cout << p2.mX << endl;
	cout << p2.mY << endl;
	return 0;
}

物件成員:

#include <iostream>
#include "Coordinate.h"
using namespace std;

class Line {
public:
//	Line();
	Line(int x1,int y1,int x2,int y2);
	~Line();
//	void setA(int x,int y);
//	void setB(int x,int y);
	Coordinate getCoorA();
	Coordinate getCoorB();
private:
	Coordinate m_A;
	Coordinate m_B;
};

#include "Line.h"

//Line::Line(){
//	cout << "Line()" << endl;
//}

Line::Line(int x1, int y1, int x2, int y2) :
		m_A(x1, y1), m_B(x2, y2) {
	cout << "Line(int x1, int y1, int x2, int y2)" << endl;
}
Line::~Line() {
	cout << "~Line()" << endl;
}

Coordinate Line ::getCoorA(){
	return m_A;
}
Coordinate Line ::getCoorB(){
	return m_B;
}

#include <iostream>
using namespace std;

class Coordinate {
public:
//	Coordinate();
	Coordinate(int x, int y);
	~Coordinate();
	void printXY();

	void setX(int x);
	void setY(int y);
	int getX();
	int getY();
private:
	int mX;
	int mY;
};

#include "Coordinate.h"

Coordinate::Coordinate(int x, int y) :
		mX(x), mY(y) {
	cout << "Coordinate()" << endl;
}
Coordinate::~Coordinate() {
	cout << "~Coordinate()" << endl;
}
void Coordinate::printXY() {
	cout << "Coordinate.x = " << mX << endl;
	cout << "Coordinate.y = " << mY << endl;
}
void Coordinate::setX(int x) {
	mX = x;
}
int Coordinate::getX() {
	return mX;
}
void Coordinate::setY(int y) {
	mY = y;
}
int Coordinate::getY() {
	return mY;
}

#include <iostream>
#include "Line.h"

using namespace std;

int main() {
	/**
	 *  包裝汽車概念理解:例項化Line時會先去例項化Line裡面的物件成員Coordinate,
	 *  銷燬則是與建立的過程相反,先銷燬Line再銷燬Coordinate
	 */
	Line *line = new Line(1,2,5,3);
	if (NULL == line) {
		return 0;
	}

	line->getCoorA().printXY();
	line->getCoorB().printXY();

	delete line;
	line = NULL;
	cout << "物件成員" << endl;
	return 0;
}

物件成員指標:

#include <iostream>
#include "Coordinate.h"
using namespace std;

class Line {
public:
//	Line();
	Line(int x1,int y1,int x2,int y2);
	~Line();
//	void setA(int x,int y);
//	void setB(int x,int y);
	Coordinate getCoorA();
	Coordinate getCoorB();
private:
	Coordinate *m_A;
	Coordinate *m_B;
};

#include "Line.h"

//Line::Line(){
//	cout << "Line()" << endl;
//}

//Line::Line(int x1, int y1, int x2, int y2) :
//		m_A(x1, y1), m_B(x2, y2) {
//	cout << "Line(int x1, int y1, int x2, int y2)" << endl;
//}

Line::Line(int x1, int y1, int x2, int y2) {
	m_A = new Coordinate(x1, y1);
	m_B = new Coordinate(x2, y2);
	cout << "Line(int x1, int y1, int x2, int y2)" << endl;
}

Line::~Line() {
	delete m_A;
	m_A = NULL;
	delete m_B;
	m_B = NULL;
	cout << "~Line()" << endl;
}

Coordinate Line::getCoorA() {
	return *m_A;
}
Coordinate Line::getCoorB() {
	return *m_B;
}

#include <iostream>
using namespace std;

class Coordinate {
public:
//	Coordinate();
	Coordinate(int x, int y);
	~Coordinate();
	void printXY();

	void setX(int x);
	void setY(int y);
	int getX();
	int getY();
private:
	int mX;
	int mY;
};

#include "Coordinate.h"

Coordinate::Coordinate(int x, int y) :
		mX(x), mY(y) {
	cout << "Coordinate()" << endl;
}

//Coordinate::Coordinate(int x, int y) {
//	mX = x;
//	mY = y;
//	cout << "Coordinate()" << endl;
//}

Coordinate::~Coordinate() {
	cout << "~Coordinate()" << endl;
}
void Coordinate::printXY() {
	cout << "Coordinate.x = " << mX << endl;
	cout << "Coordinate.y = " << mY << endl;
}
void Coordinate::setX(int x) {
	mX = x;
}
int Coordinate::getX() {
	return mX;
}
void Coordinate::setY(int y) {
	mY = y;
}
int Coordinate::getY() {
	return mY;
}

#include <iostream>
#include "Line.h"
using namespace std;

int main() {
	Line *line = new Line(1, 2, 5, 3);
	if (NULL == line) {
		return 0;
	}

	line->getCoorA().printXY();
	line->getCoorB().printXY();

	delete line;
	line = NULL;

	cout << sizeof(line)<< endl;// 指標大小4
	cout << sizeof(Line)<< endl;// 類的大小(類裡面有兩個指標)8

	cout << "物件成員指標:即Line裡面的成員物件Coordinate是指標型別的" << endl;
	return 0;
}

物件陣列:

#include <iostream>
using namespace std;

class Coordinate {
public:
	Coordinate();
	~Coordinate();
public:
	int mX;
	int mY;
};

#include "Coordinate.h"

Coordinate::Coordinate() {
	cout << "Coordinate()" << endl;
}
Coordinate::~Coordinate() {
	cout << "~Coordinate()" << endl;
}

#include <iostream>
#include "Coordinate.h"
using namespace std;

int main() {
	Coordinate c[3];
	c[0].mX = 10;
	cout << "c[0].mX = " << c[0].mX << endl;

	Coordinate *coor = new Coordinate[3];
	if (NULL == coor) {
		return 0;
	}

	//  指標方式訪問元素
	coor[0].mX = 15;
	coor->mY = 30;
	(coor + 1)->mY = 60;
	cout << "coor[0].mX = " << coor[0].mX << endl;
	cout << "coor[0].mY = " << coor[0].mY << endl;
	cout << "coor[1].mY = " << coor[1].mY << endl;

	coor++; //注意指標此時已經指向第1位元素下標
	coor->mX = 0;
	cout << "coor[1].mX = " << coor->mX << endl;

	// 因為上面執行了coor++,所以直接delete無法釋放之前申請的記憶體,必須要跟申請時的記憶體段保持一致
	coor--;
	delete[] coor;
	coor = NULL;
	return 0;
}

物件的深拷貝和淺拷貝:

#include <iostream>
using namespace std;

// 計算陣列長度
template<class T>
int length(T& arr) {
	//cout << sizeof(arr[0]) << endl;
	//cout << sizeof(arr) << endl;
	return sizeof(arr) / sizeof(arr[0]);
}

/**
 * 淺拷貝demo class
 */
class Array {
public:
	Array() {
		mConut = 5;
		mpArr = new int[mConut];
		cout << "Array()" << endl;
	}
	Array(const Array&arr) {
		mConut = arr.mConut;
		mpArr = arr.mpArr;
		cout << "&Array()" << endl;
	}
	~Array() {
		delete[] mpArr;
		mpArr = NULL;
		cout << "~Array()" << endl;
	}
	void print() {
		cout << "_mConut = " << mConut << endl;
		cout << "_mpArr.length = " << length(mpArr) << endl;
	}

	void printAddr() {
		cout << "mpArr = " << mpArr << endl;
	}

	int getCount() {
		return mConut;
	}
private:
	int *mpArr;
	int mConut = 6;
};

/**
 * 深拷貝demo class
 */
class ArrayS {
public:
	ArrayS() {
		mConut = 5;
		mpArr = new int[mConut];
		cout << "ArrayS()" << endl;
	}
	ArrayS(const ArrayS&arrs) {
		mConut = arrs.mConut;
//		mpArr = arrs.mpArr;
		mpArr = new int[mConut]; //重新開闢一塊記憶體出來
		for (int i = 0; i < mConut; i++) {
			mpArr[i] = arrs.mpArr[i];
		}
		cout << "&ArrayS()" << endl;
	}
	~ArrayS() {
		delete[] mpArr;
		mpArr = NULL;
		cout << "~ArrayS()" << endl;
	}
	void print() {
		cout << "_mConut = " << mConut << endl;
		cout << "_mpArr.length = " << length(mpArr) << endl;
	}
	void printAddr() {
		cout << "mpArr = " << mpArr << endl;
	}

	int getCount() {
		return mConut;
	}
private:
	int *mpArr;
	int mConut = 6;
};

/**
 * 拷貝建構函式的深淺拷貝
 *
 * 注意:與java不同,物件的賦值,兩個物件不是等價的,
 *
 * 屬性成員若有初始值則屬性成員會等價賦值,若未賦值,則值是未知數,屬性成員不等於另外一個物件的屬性成員,需要去拷貝建構函式手動賦值
 *
 * 物件賦值,被賦值的物件不執行建構函式,只執行拷貝建構函式
 */
int main() {
	/**
	 * 1、淺拷貝:簡單的將值拷貝
	 * 2、深拷貝:需要將拷貝值拷貝到當前物件開闢的記憶體塊中
	 *
	 * arr2和arr1的資料成員mpArr指標都是指向同一塊記憶體,在我們銷燬arr1的時候會去釋放mpArr,銷燬arr2的時候也會去釋放mpArr
	 * 那麼同一塊記憶體被釋放兩次 ,則計算機會崩潰
	 *
	 * 正確做法是要為arr2的mpArr指標重新開闢新的記憶體塊,即是深拷貝
	 *
	 * 深拷貝實際上就是要考慮到面向過程的變數或者說是記憶體塊
	 */

	// 淺拷貝 同一塊記憶體釋放了2次
	Array arr1;
	Array arr2 = arr1;
//	arr1.print();
//	arr2.print();
	arr1.printAddr();
	arr2.printAddr();
	cout << "arr2_mConut = " << arr2.getCount() << endl;

	// 深拷貝
	ArrayS arrS1;
	ArrayS arrS2 = arrS1;
	arrS1.printAddr();
	arrS2.printAddr();
	return 0;
}

this指標的應用:

#include <iostream>
using namespace std;

class Array {
public:
	Array() {
		cout << "Array()" << endl;
	}
	~Array() {
		cout << "~Array()" << endl;
	}
	Array(Array&arr) {
		cout << "&Array()" << endl;
	}
	void setLen(int len) {
		this->len = len;
	}
	int getLen() {
		return this->len;
	}
	Array print1() { // 注意:看列印資訊可知拷貝建構函式被呼叫一次,解構函式多執行一次,說明返回出去一個臨時的物件
		cout << " print1()" << endl;
		return *this; //返回的this是一個指標,this不是一個物件
	}
	Array& print2() { // 返回的是一個物件的引用,則操作的是同一個物件
		cout << " print2()" << endl;
		return *this;
	}
	Array* print3() { // 返回的是一個物件的指標,則操作的也是同一個物件
		cout << " print3()" << endl;
		return this;
	}

	void printAddr() { //輸出當前物件的記憶體地址
		cout << this << endl;
	}
private:
	int len;
};

/**	一、	物件是如何儲存在記憶體中的?
 * 			記憶體分割槽
 * 1、棧區 	int i = 1;定義一個變數
 * 2、堆區	int *p = new int[10];
 * 3、全域性區	儲存的全域性變數和靜態變數
 * 4、常量區	string str = "c++"
 * 5、程式碼區	儲存邏輯程式碼的二進位制
 */

/**
 * this指標的應用,與java中的this類似
 *
 * this指標預設已經在每個函式的引數列表
 *
 * A a1
 * A a2
 * A a3
 * 我們用物件a1,a2,a3呼叫類A中的同一個方法時 ,實際上在記憶體管理的程式碼區中只儲存了一次這個方法的二進位制邏輯程式碼,
 * 但是為什麼能區分當前是執行哪個物件的這個方法?因為方法引數列表中預設有代表當前物件的this指標
 *
 *
 */
int main() {

	Array arr1;
	arr1.setLen(10);
	cout << " len " << arr1.getLen() << endl;
	arr1.print1().setLen(5);//不能改變arr1的len值
	cout << " len " << arr1.getLen() << endl;
	arr1.print2().setLen(5);
	cout << " len " << arr1.getLen() << endl;
	arr1.print3()->setLen(6);
	cout << " len " << arr1.getLen() << endl;

	arr1.printAddr();
	cout << &arr1 << endl;
	return 0;
}