c++類和物件的詳細介紹(二)
一,物件的動態建立和釋放
1.什麼是物件的動態建立和釋放
通常我們建立的物件都是由C++編譯器為我們在棧記憶體中建立的,我們無法對其進行生命週期的管理。所以我們需要動態的去建立該物件,因此我們需要在堆記憶體中建立物件和釋放物件。在C語言中為我們提供了malloc()函式和free()函式來為我們提供在堆記憶體中分配變數的方式,但是在C++中引入了new和delete關鍵字來讓我們動態的建立和釋放變數。
2.new和delete關鍵字
new關鍵字是用來在堆記憶體中建立變數的,格式為:Type * ptr = new Type(常量/表示式); 其引數列表中的常量/表示式可以用來給變數初始化,也可以省略不寫。其返回結果為該型別的指標。如果記憶體分配失敗則返回空指標。
delete關鍵字是用來釋放用new關鍵字建立的記憶體,格式為delete ptr(釋放陣列必須需要加中括號,delete [] ptr)。
3.new和delete關鍵字與malloc和free的區別
new關鍵字在分配記憶體的時候,會根據其建立的引數呼叫相應的類的建構函式。delete關鍵字會在釋放記憶體之前,會首先呼叫類的解構函式釋放物件中定義的記憶體。
malloc和free關鍵字不會去呼叫類的建構函式和解構函式。
4.new和delete關鍵字示例
# define _CRT_SECURE_NO_WARNINGS # include<iostream> using namespace std; class Teacher { public: char * name; int age; public: /* 無參建構函式 */ Teacher() { name = NULL; age = 0; cout << "無參建構函式被執行..." << endl; } /* 有參建構函式 */ Teacher(char * name, int age) { /* 在建構函式中分配堆記憶體 */ this->name = new char[sizeof(name) + 1]; /* 初始化成員變數 */ strcpy(this->name, name); this->age = age; cout << "有參建構函式被執行..." << endl; } /* 拷貝建構函式 */ Teacher(const Teacher &student) { /* 重新分配記憶體 */ this->name = new char[sizeof(name) + 1]; /* 初始化成員變數 */ strcpy(this->name, name); this->age = age; cout << "拷貝建構函式被執行..." << endl; } /* 解構函式 */ ~Teacher() { if (this->name != NULL) { delete [] this->name; this->name = NULL; this->age = 0; } cout << "解構函式被執行..." << endl; } }; int main() { /* 建立int變數,並釋放 */ int * a = new int; int * b = new int(100); delete a; delete b; /* 建立double變數,並釋放 */ double * c = new double; double * d = new double(10.1); delete c; delete d; /* 建立陣列並釋放 */ char * e = new char[100]; delete [] e; /* 建立物件並釋放 */ Teacher * stu1 = new Teacher("王剛",22); cout << "姓名:" << stu1->name << ",年齡:" << stu1->age << endl; Teacher * stu2 = new Teacher(); delete stu1; delete stu2; /* 利用malloc和free建立物件,無法呼叫其構造和解構函式*/ Teacher * stu3 = (Teacher *)malloc(sizeof(Teacher)); free(stu3); }
二,靜態成員變數和靜態成員函式
1.static關鍵字
static關鍵字用來宣告類中的成員為靜態屬性。當用static關鍵字修飾成員後,該類所建立的物件共享static成員。無論建立了多少個物件,該成員只有一份例項。靜態成員是與類相關的,是類的一種行為,而不是與該類的物件相關。
2.靜態成員的概念
靜態成員是類所有的物件的共享成員,而不是某個物件的成員,它在物件中不佔用儲存空間,這個成員屬於整個類,而不屬於具體的一個物件,所以靜態成員變數無法在類的內部進行初始化,必須在類的外部進行初始化。比如定義一個學生類,那麼學生物件總數可以宣告為static,在構造方法中,對該變數進行加1,從而統計學生物件的數量。
3.靜態成員變數總結
靜態成員變數可以用static關鍵字定義,但是初始化必須在類的外面進行初始化。
靜態成員變數可以被類及類的物件所訪問和修改。
靜態成員變數遵循類的訪問控制原則,如果為private修飾,則只可以在類的內部和在類外面初始化的時候訪問,不會再被其他方式訪問。
4.靜態成員函式總結
靜態成員函式用static關鍵字定義,在靜態成員函式中可以訪問靜態成員變數和靜態成員函式,但不允許訪問普通的成員變數和成員函式,因為普通的成員屬於物件而不屬於類。層次不一樣。但是在普通成員中可以訪問靜態成員。
當靜態成員函式在類中定義,但是在類的外面實現的時候,不需要再加static關鍵字。
靜態成員函式沒有this指標。
5.靜態成員重點歸納
靜態成員是類和類的物件的所有者,因此靜態成員變數不能在類的內部進行初始化,必須在類的外部進行初始化。
靜態成員依舊遵循private,protected,public的訪問控制原則。
靜態成員函式中沒有this指標,不能訪問普通的成員變數和成員函式,可以訪問靜態成員變數和成員函式,但是可以通過傳遞物件的方式訪問普通成員。
6.靜態成員變數演示
# include<iostream>
using namespace std;
class MyStudent
{
private:
static int count;/* 學生物件總數 */
char name[64];
int age;
public:
static int n;
public:
MyStudent(char * name,int age)
{
strcpy(this->name, name);
this->age = age;
MyStudent::count++;/* 學生數量加1 */
}
void getCount()/* 普通成員函式訪問靜態成員變數 */
{
cout << "學生總數:" << MyStudent::count << endl;
}
};
/* 靜態成員變數初始化 */
int MyStudent::count = 0;
int MyStudent::n = 10;
int main()
{
/* 測試靜態成員變數 */
MyStudent student1("王剛",22);
student1.getCount();
/* 物件和類方式訪問靜態成員變數 */
student1.n = 100;
MyStudent::n = 200;
}
複製程式碼
7.靜態成員函式演示
複製程式碼
# include<iostream>
using namespace std;
class Test
{
private:
int m;
public:
static int n;
public:
void setM(int m)
{
this->m = m;
/* 訪問靜態成員函式 */
test();
}
public:
static void xoxo();
static void test()
{
n = 100;
// m = 10; 不允許訪問普通成員變數
// int c = getM(); 不允許訪問普通成員函式
// this->m = 1000; this指標不存在
cout << "static void test()函式..." << endl;
}
};
/* 初始化靜態成員 */
int Test::n = 10;
/* 類中宣告,類外實現 */
void Test::xoxo()
{
cout << "static void Test::xoxo" << endl;
}
int main()
{
Test t;
/* 普通成員函式訪問靜態成員函式 */
t.setM(10);
/* 成員函式的呼叫方式 */
t.test();
Test::test();
}
三,友元函式和友元類
1.友元函式
當我們定義類的時候,使用private關鍵字修飾成員變數(成員函式),這樣做到了訪問控制。有些時候,我們需要讓一些函式來訪問物件的私有成員(屬性或方法),C++為我們提供了友元函式這個概念,所謂的友元函式就是指這個函式是這個類的好朋友,允許讓這個函式訪問這個類建立的物件的私有屬性和私有方法。友元函式用friend函式來宣告,友元函式的宣告必須在類的內部,友元函式的實現必須要在類的外部(如果友元函式的實現也在內部,那還要用友元函式幹什麼?),友元函式的宣告位置與訪問控制符無關。
2.友元函式示例
# include<iostream>
using namespace std;
/* 定義點類 */
class Point
{
private:
int x;
int y;
/* 友元函式的定義:求兩點的距離 */
friend int distance(Point &p1, Point &p2);
public:
Point(int x, int y)
{
this->x = x;
this->y = y;
}
};
/* 友元函式的實現 */
int distance(Point &p1, Point &p2)
{
int dx = p1.x - p2.x;
int dy = p1.y - p2.y;
return sqrt(dx*dx + dy*dy);
}
int main()
{
Point p1(3, 4);
Point p2(0, 0);
int dis = distance(p1, p2);
cout << "點(3,4)到原點的距離為:" << dis << endl;
}
3.友元類
若B類是A類的友元類,則B類的所有成員函式都是A類的友元函式。類B可以訪問類A的所有私有屬性和方法。
友元類通常被設計為一種對資料操作或者類之間傳遞訊息的輔助類。
4.友元類示例
# include<iostream>
using namespace std;
/* 定義類A */
class A
{
private:
int x;
friend class B;/* 定義類B為類A的友元類 */
private:
void setX(int x)
{
this->x = x;
}
};
/* 定義類B */
class B
{
private:
A AObj;
public:
/* 類B的所有成員函式都是類A的友元函式,因此都可以訪問類A的私有屬性和方法 */
void operater(int tmp)
{
AObj.setX(tmp);
}
void display()
{
cout << "類A的私有屬性x = " << AObj.x << endl;
}
};
int main()
{
B b;
b.operater(100);
b.display();
return 0;
}
四,C++中引用的基礎知識
1.引用的基本概念
1.所謂的引用其實就是對變數起“別名”。引用和變數對應得是相同的記憶體,修改引用的值,變數的值也會改變,和指標類似。
2.引用在定義的時候必須要初始化,初始化後就用引用的物件繫結在一起了。
3.引用本身不是物件,不能定義引用的引用。
2.引用的意義
1.引用作為其他變數的別名存在,因此在一些場合可以用來替代指標。
2.引用相對於指標來說具有更好的可讀性和實用性。
3.引用的定義方式
資料型別 &引用名稱 = 變數;
# include<iostream>
using namespace std;
int main()
{
// 定義變數a
int a = 10;
// 定義引用b
int &b = a;
return 0;
}
二,引用的本質
1.引用的本質剖析
# include<iostream>
using namespace std;
int main()
{
// 定義變數a
int a = 10;
// 定義引用b
int &b = a;
// 引用一旦初始化,就不再改變本身所繫結的物件,因此引用很想一個const修飾的常量
cout << "&a = " << &a << endl;
cout << "&b = " << &b << endl;
// 通過列印地址,發現變數和引用的地址相同,因此引用本質是一個指標
return 0;
}
2.引用的本質分析
1.引用的初始化之後不允許被修改,因此引用是一個常量。
2.引用的地址和變數本身的地址一樣。因此引用是一個指標。
3.引用的本質是一個常量指標,只是C++編譯器幫助我們進行了自動取地址操作和解引用操作。
4.上述的案例的本質:int &b = a; 相當於 int * const b = &a;
三,引用的重點
1.引用作函式引數
引用作函式引數可以用來替代指標,在函式內部操作引用,就可以修改函式外部變數的值。
2.引用作函式返回值(此處涉及類的拷貝建構函式)
引用作函式返回值,如果返回的是棧變數,則實際返回的是該變數的一份拷貝。
3.指標引用
引用是指標型別的,例如:Teacher teacher = {“王剛”,21};Teacher * &t = &teacher;
4.常引用
使用變數初始化引用,使引用具有隻讀屬性,保護了引用所指向的物件,防止被函式內部修改。
使用字面量初始化引用,例如:const int &a = 10;是可以的,這裡會在編譯期間對字面量常量分配記憶體空間。
五,名稱空間(namespace)的基本概念以及由來
1.什麼是識別符號:
在C++中,識別符號可以是基本的變數,類,物件,結構體,函式,列舉,巨集等。
2.什麼是名稱空間:
所謂的名稱空間是指識別符號的可見範圍。C++標準庫中的所有的識別符號都被定義在一個名為std的名稱空間中。
3.C語言的名稱空間:
在C語言中只有一個全域性作用域,因此在C語言中所有的識別符號共享一個名稱空間,因此隨著程式碼量的增大,識別符號之間可能會重名。由此會造成一些命名問題。
4.針對C語言的問題,在C++中提出了名稱空間的概念:
1.名稱空間將全域性作用域劃分成不同的的部分。
2.不同的名稱空間中的識別符號可以重名而不會發生衝突。
3.全域性作用域也叫做預設名稱空間。
4.名稱空間之間可以相互巢狀。
二,名稱空間(namespace)的定義以及使用
1.名稱空間的定義:
namespace 名稱空間名稱
{
……
}
2.名稱空間的使用:
using namespace name; // 使用name這個名稱空間
using namespace name::variable; // 使用name這個名稱空間下的variable
std::cout << “Hello,NameSpace” << std::endl; // 直接使用這個名稱空間中的識別符號
::tmp = 0; // 使用預設名稱空間的變數,預設情況下可以直接使用預設名稱空間的所有識別符號
3.域作用符::
域作用符用兩個冒號(::)表示,用來訪問名稱空間下的識別符號。
三,名稱空間(namespace)實際案例
# include<iostream>
/* 定義名稱空間n1 */
namespace n1
{
/* 定義學生類 */
class Student
{
public:
char name[64];
int age;
};
/* 定義函式 */
void printN1()
{
std::cout << "我是n1名稱空間的printN1()" << std::endl;
}
/* 定義名稱空間n2 */
namespace n2
{
/* 定義變數n */
int n = 0;
}
}
// 在預設名稱空間下定義變數a
int a = 100;
int main()
{
// 使用名稱空間n1,曝光n1的所有識別符號,因此可以呼叫printN1()函式
using namespace n1;
printN1();
// 使用名稱空間n2,曝光n2的所有識別符號,因此可以使用變數n
using namespace n1::n2;
// 直接使用指定名稱空間下的識別符號
std::cout << n << std::endl;
// 使用域作用符訪問預設名稱空間下的識別符號
::a = -100;
}