2023/6/18
本篇章記錄學習程序C++的基礎概念和代碼測驗實作,還有很多需要補充,一是還不清楚,二是還沒有學到,打算學習程序中后面再做補充,先看完《C++primer 》書之后再慢慢來添加補充
1.函式多載
- 一個函式名可以實作多個功能,這取決于函式引數不同來實作判斷對應的功能,與回傳值無關
- 函式可以多載,建構式,成員函式都可以多載,但是,解構式不能多載
#include <iostream>
using namespace std;
void print()
{
cout << "沒有引數的print函式" << endl;
}
//int print() 錯誤
//{
// cout << "沒有引數的print函式" << endl;
//}
void print(int a)
{
cout << "一個int引數的print函式"<< a << endl;
}
void print(string s)
{
cout << "一個string引數的print函式" << s << endl;
}
void print(int a,int b)
{
cout << "兩個int引數的print函式" << a << b << endl;
}
int main()
{
// 通過傳入引數的不同,可以呼叫不同的多載函式
print(1);
print(1,2);
print();
print("hjahhah");
return 0;
}
2.函式默認引數
函式可以設定默認值,當呼叫函式時,可以不傳遞引數,這樣就會使用默認值,
注意點:
- 函式宣告與定義分離,函式的引數默認值可以寫在宣告或定義處,但是只能出現一次
- 遵循向右原則,這個原則就是說某個引數設定了默認值,那么它的右邊引數必須設定默認值
- 默認引數與函式多載一起使用時,需要注意不能發生二義性
void add(int a =1,int b=2,int c=3);
add(5);//這樣是a為5,其余是默認b=2,c=3
add(7,8);//這樣a=7,b=8,默認值c=3;
向右原則就是這樣,a如果是默認值,那么b和c一定是默認值,不能b為默認值,c為傳參,
#include <iostream>
using namespace std;
void show(int a = 1,int b = 2,int c = 3)
{
cout << a << " " << b << " " << c << endl;
}
void print(int a,int b);
void print(int a=1,int b =1)
{
cout << a << " " << b << endl;
}
void print2(int a = 1);
void print2(int a)
{
cout << a << endl;
}
int main()
{
show(); // 1 2 3
show(6); // 6 2 3
show(5,6); // 5 6 3
show(6,7,8); // 6 7 8
print(); // 1 1
print(2); // 2 1
print(2,3); // 2 3
print2(); // 1
print2(2); // 2
return 0;
}
#include <iostream>
using namespace std;
void show(int a = 1,int b = 2,int c = 3)
{
cout << a << " " << b << " " << c << endl;
}
void show()
{
cout << "哈哈哈哈哈" << endl;
}
int main()
{
// show(); 錯誤:二義性
return 0;
}
3.參考
&可以改變參考的變數的值
注意點:
- 可以改變參考值,但是不能再次成為其他變數的參考
- 宣告參考時,需要初始化
- 初始化的值不能為NULL
- 初始值是純數字,需要加const關鍵字來修飾參考,表示參考的值不可變
- 可以將變數參考的地址賦值給一個指標,此處指標指向的還是原來的變數
- 可以對指標建立參考
- 使用const關鍵詞修飾參考,此時不能通過參考修改數值,但是可以修改參考原變數的數值
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int b = 2;
int& c = a; // c是a的參考
c = b; // 把b的值賦給c
cout << a << " " << &a << endl; // 2 0x61fe88
cout << b << " " << &b << endl; // 2 0x61fe84
cout << c << " " << &c << endl; // 2 0x61fe88
// int& c = b; 錯誤 變數c已經參考了a,現在再參考b
// &c = b; 錯誤
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a = 1;
// int& b; 錯誤
b = a;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
// int& a = NULL; 錯誤
return 0;
}
#include <iostream>
using namespace std;
int main()
{
// 常參考
const int &a = 123;
// a++; 錯誤
cout << a << endl; // 123
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int &b = a;
int* c = &b; // 指標c指向b
a++;
cout << *c << endl; // 2
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int* b = &a; // b是a的指標
int*& c = b; // c是b的參考
cout << &a << " " << a << endl; // 0x61fe88 1
cout << b << " " << *b << endl; // 0x61fe88 1
cout << c << " " << *c << endl; // 0x61fe88 1
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a = 1;
const int &b = a; // b是a的常參考
// b++; 錯誤
a++;
cout << b << endl; // 2
return 0;
}
4.物件
物件是面向物件編程思想的核心,面向物件的三個基本特征:封裝、繼承、多型
這三個就是核心了,要講清楚需要的篇章太多了,
4.1創建物件
創建物件的前提是有一個類,然后就選擇是堆疊記憶體物件 還是 堆記憶體物件
- 堆疊記憶體物件生命周期在所在的{}結束后,自動銷毀
- 堆記憶體物件在
delete關鍵字銷毀,不手動銷毀,物件持續存在
class Witch//創建了類
{
}
int main()
{
Witch con1;//con1為物件,堆疊記憶體物件
Witch* con2=new Witch//con2物件 ,堆記憶體物件
}
4.2封裝的體現
class Witch//創建了類
{
private: // 私有權限:只能在類內部訪問
public: //公有權限,全域,派生類,類內都可以訪問,派生類就是繼承的類
protected: //保護權限,全域無法訪問
}
|
| 類內訪問 | 派生類內訪問 | 全域訪問 |
| --- | --- | --- | --- |
| 私有權限 private | √ | X | X |
| 保護權限 protected | √ | √ | X |
| 公有權限 public | √ | √ | √ |
使用prvite將類中成員隱藏,開放public介面公開訪問
#include <iostream>
using namespace std;
/**
* @brief 所有類名都要使用帕斯卡(大駝峰)命名法,即所有單詞的首字母使用大寫
*/
class MobilePhone
{
private: // 私有權限:只能在類內部訪問
string brand; // 可讀可寫
string model; // 只寫
int weight = 188; // 只讀,賦予了初始值
public:
string get_brand() // 讀函式:getter
{
return brand;
}
void set_brand(string b) // 寫函式:setter
{
brand = b;
}
void set_model(string m) // setter
{
model = m;
}
int get_weight() // getter
{
return weight;
}
};
int main()
{
MobilePhone mp1;
mp1.set_brand("華為");
mp1.set_model("P60");
cout << mp1.get_brand() << endl;
cout << mp1.get_weight() << endl;
MobilePhone* mp2 = new MobilePhone;
mp2->set_brand("魅族");
mp2->set_model("20");
cout << mp2->get_brand() << endl;
cout << mp2->get_weight() << endl;
delete mp2;
return 0;
}
4.2.1建構式
創建類物件,代碼就會進入建構式,沒寫,默認一個建構式
建構式有以下特點:
- 函式名稱與類名完全相同
- 建構式不寫回傳值
- 建構式支持函式多載
- 建構式可以使用初始化串列賦值
class Witch//創建了類
{
Witch()//建構式
{ }
Witch(int a)//建構式函式多載
{ }
Witch(int a,string b):num(a),modl(b)//初始化串列,num=a,modl=b;
{ }
private: // 私有權限:只能在類內部訪問
int num=0;
string modl;
public: //公有權限,全域,派生類,類內都可以訪問,派生類就是繼承的類
protected: //保護權限,全域無法訪問
}
4.2.2拷貝建構式
每個類提供一個多載的拷貝建構式,用于物件的拷貝,即基于某個物件創建一個資料完全相同的物件,注意點:
- 新創建的物件與原來物件是兩個物件
- 淺拷貝(當類中出現了指標型別的成員變數時,默認的拷貝建構式會造成淺拷貝的問題,不同物件的成員變數會指向同一個區域,不符合面向物件的設計,)
- 深拷貝(解決淺拷貝)
- 隱式呼叫建構式(使用explicit修飾建構式后,就只支持顯示呼叫了)
class Witch//創建了類
{
Witch()//建構式
{ }
Witch(const Witch& w)//默認的拷貝建構式
{ }
}
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char* name;
public:
Dog(char* n)
{
name = n;
}
// 復原淺拷貝(寫不寫都行)
Dog(const Dog& d)
{
name = d.name;
}
void show()
{
cout << name << endl;
}
};
int main()
{
char c[20] = "wangcai";
Dog d1(c);//建構式
// 拷貝建構式
Dog d2(d1);
strcpy(c,"xiaobai");
d1.show(); // xiaobai
d2.show(); // xiaobai
//出現了指向同一位置導致資料發生改變
return 0;
}
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char* name;
public:
Dog(char* n)
{
// 單獨開辟一塊堆記憶體
name = new
strcpy(name,n);
}
// 深拷貝
Dog(const Dog& d)
{
// 單獨開辟一塊堆記憶體
name = new char[20];
strcpy(name,d.name);
}
void show()
{
cout << name << endl;
}
};
int main()
{
char c[20] = "wangcai";
Dog d1(c);
// 拷貝建構式
Dog d2(d1);
strcpy(c,"xiaobai");
d1.show(); // wangcai
d2.show(); // wangcai
return 0;
}
#include <iostream>
using namespace std;
class Teacher
{
private:
string name;
public:
// 使用explicit修飾建構式后,只支持顯式呼叫
Teacher(string n)
{
cout << "創建了一個老師" << endl;
name = n;
}
string get_name()
{
return name;
}
};
int main()
{
string name = "羅翔";
// 隱式呼叫建構式
Teacher t = name; // 創建了一個老師物件
cout << t.get_name() << endl; // 羅翔
return 0;
}
4.2.3解構式
解構式是類中與建構式完全對立的函式,
| 建構式 | 解構式 |
|---|---|
| 手動呼叫后創建物件 | 物件銷毀時自動呼叫 |
| 可以有引數,支持多載和引數默認值 | 無引數 |
| 函式名稱是類名 | 函式名稱是~類名 |
| 通常用于物件創建時初始化 | 通常用于物件銷毀時回收記憶體和資源 |
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char* name;
public:
Dog(char* n)
{
// 單獨開辟一塊堆記憶體
name = new char[20];
strcpy(name,n);
}
// 復原淺拷貝(寫不寫都行)
Dog(const Dog& d)
{
// 單獨開辟一塊堆記憶體
name = new char[20];
strcpy(name,d.name);
}
void show()
{
cout << name << endl;
}
// 解構式
~Dog()
{
delete name; //這里加上,就解決了深拷貝開辟空間的釋放問題
}
};
int main()
{
char c[20] = "wangcai";
Dog d1(c);
// 拷貝建構式
Dog d2(d1);
strcpy(c,"xiaobai");
d1.show(); // wangcai
d2.show(); // wangcai
return 0;
}
4.2.4.類內宣告,類外定義
#include <iostream>
using namespace std;
class Student
{
private:
string name = "張三";
public:
// 宣告
void study();
};
// 定義
void Student::study()
{
cout << name << "在努力學習!" << endl;
}
int main()
{
Student s;
s.study(); // 張三在努力學習!
return 0;
}
4.2.5.this指標
是一種特殊的指標,只能在類的成員函式,建構式,解構式中使用,this指標指向當前類的物件首地址
作用:
- 區磁區域變數和成員變數
- 鏈式呼叫
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << this << endl;
}
};
int main()
{
Test t1; // 0x61fe7b
cout << &t1 << endl; // 0x61fe7b
Test* t2 = new Test; // 0x8f0fe0
cout << t2 << endl; // 0x8f0fe0
delete t2;
return 0;
}
#include <iostream>
using namespace std;
class Computer
{
private:
string brand;
public:
Computer(string brand)
{
// 區分重名
this->brand = brand;
}
string get_brand()
{
// 所有的成員在類內部呼叫都是靠this指標,平常可省略
return this->brand;
}
void show()
{
// 所有的成員在類內部呼叫都是靠this指標,平常可省略
cout << this->get_brand() << endl;
}
};
int main()
{
Computer c("聯想");
cout << c.get_brand() << endl; // 聯想
c.show(); // 聯想
#include <iostream>
using namespace std;
class Number
{
private:
int value;
public:
Number(int value):value(value) // 重名時使用構造初始化串列也可以
{}
Number& add(int v) // 支持鏈式呼叫
{
value += v;
return *this; // 固定用法
}
int get_value()
{
return
;
}
};
int main()
{
Number n1(1);
// 傳統方式:非鏈式呼叫
n1.add(2);
n1.add(3);
n1.add(4);
cout << n1.get_value() << endl; // 10
Number n2(1);
// 鏈式呼叫
cout << n2.add(2).add(3).add(4).get_value() << endl; // 10
// string類的append函式在源代碼中也采用了鏈式呼叫的設計
string s = "A";
cout << s.append("B").append("C").append("D") << endl; // ABCD
return 0;
}
4.3繼承的體現
繼承是在一個已經存在的類的繼承上建立一個新的類,新的類擁有已經存在的類的特性,已經存在的類被稱為“基類”或“父類”;新建立的類被稱為“派生類”或“子類”,
class Father
{
}
class Son:public Father
{}
4.3.1析構與構造不能被繼承
在繼承中,任何一個派生類的任意一個建構式都必須之間或間接呼叫基類的任意一個建構式,因為在創建派生類物件時,需要呼叫基類的代碼,使用基類的邏輯去開辟部分繼承的空間
#include <iostream>
using namespace std;
class Father
{
private:
string first_name;
public:
Father(string first_name)
{
this->first_name = first_name;
}
string get_first_name() const
{
return first_name;
}
};
class Son:public Father
{
};
int main()
{
// Son s1("張"); 錯誤:建構式不能被繼承
// Son s1; 錯誤:找不到Father::Father()建構式
return 0;
}
4.3.2派生類建構式呼叫基類建構式
- 透傳構造
- 委托構造
- 繼承構造
4.3.2.1透傳構造
透傳構造是派生類的建構式中直接呼叫基類的建構式

#include <iostream>
using namespace std;
class Father
{
private:
string first_name;
public:
Father(string first_name)
{
this->first_name = first_name;
}
string get_first_name() const
{
return first_name;
}
};
class Son:public Father
{
public:
// Son():Father(){} 如果不寫建構式,編譯器自動添加
Son():Father("王"){} // 透傳構造
Son(string name)
:Father(name){} // 透傳構造
};
int main()
{
Son s1;
cout << s1.get_first_name() << endl;
Son s2("張");
cout << s2.get_first_name() << endl;
return 0;
}
4.3.2.2委托構造
同一個類中的建構式可以呼叫另外一個多載建構式,注意點是,最終必須有一個建構式透傳呼叫基類的建構式

#include <iostream>
using namespace std;
class Father
{
private:
string first_name;
public:
Father(string first_name)
{
this->first_name = first_name;
}
string get_first_name() const
{
return first_name;
}
};
class Son:public Father
{
public:
Son():Son("王"){} // 委托構造
Son(string name)
:Father(name){} // 透傳構造
};
int main()
{
Son s1;
cout << s1.get_first_name() << endl;
Son s2("張");
cout << s2.get_first_name() << endl;
return 0;
}
4.3.2.3繼承構造
繼承構造是C++11的特性,并不是真正的繼承建構式,而是編譯器自動為派生類創建n個建構式,

#include <iostream>
using namespace std;
class Father
{
private:
string first_name;
public:
Father(string first_name)
{
this->first_name = first_name;
}
Father():Father("張"){}
string get_first_name() const
{
return first_name;
}
};
class Son:public Father
{
public:
// 一句話搞定
using Father::Father;
// 相當于添加了下面的代碼
// Son(string first_name):
// Father(first_name){}
// Son():Father(){}
};
int main()
{
Son s1;
cout << s1.get_first_name() << endl;
Son s2("張");
cout << s2.get_first_name() << endl;
return 0;
}
4.3.3.物件的創建和銷毀流程
- 創建與銷毀流程完全對稱,
2. 創建程序中,同型別功能都是基類先執行,派生類后執行,
3. 靜態成員變數的生命周期與程式運行的周期相同,
通過上述例子,可以看到面向物件編程的特點:撰寫效率高,執行效率低,
#include <iostream>
using namespace std;
/**
* @brief The Value class 作為其他類的變數
*/
class Value
{
private:
string name;
public:
Value(string name):name(name)
{
cout << name << "創建了" << endl;
}
~Value()
{
cout << name << "銷毀了" << endl;
}
};
class Father
{
public:
Value value = https://www.cnblogs.com/moveddown/archive/2023/06/18/Value("Father類的成員變數");
static Value s_value;
Father()
{
cout << "Father類的建構式" << endl;
}
~Father()
{
cout << "Father類的解構式" << endl;
}
};
Value Father::s_value = https://www.cnblogs.com/moveddown/archive/2023/06/18/Value("Father類的靜態成員變數");
class Son:public Father
{
public:
Value value2 = Value("Son類的成員變數");
static Value s_value2;
Son():Father()
{
cout << "Son類的建構式" << endl;
}
~Son()
{
cout << "Son類的解構式" << endl;
}
};
Value Son::s_value2 = Value("Son類的靜態成員變數");
int main()
{
cout << "程式開始執行" << endl;
{
Son s;
cout << "物件s使用中......" << endl;
}
cout << "程式結束執行" << endl;
return 0;
}

4.3.4.多重繼承
即一個派生類可以有多個基類
#include <iostream>
using namespace std;
class Sofa
{
public:
void sit()
{
cout << "能坐著!" << endl;
}
};
class Bed
{
public:
void lay()
{
cout << "能躺著!" << endl;
}
};
class SofaBed:public Sofa,public Bed
{
};
int main()
{
SofaBed sb;
sb.sit();
sb.lay();
return 0;
}
4.3.4.1二義性問題
- 多個基類擁有重名的成員時,會出現二義性
- 菱形繼承,如果一個派生類的多個基類擁有共同的基類,這種情況就是菱形繼承(通過虛繼承實作)

#include <iostream>
using namespace std;
class Furniture // 家具
{
public:
void show()
{
cout << "這是個家具" << endl;
}
};
class Bed:public Furniture // 床
{
};
class Sofa:public Furniture // 沙發
{
};
class SofaBed:public Bed,public Sofa // 沙發床
{
};
int main()
{
SofaBed sb;
// sb.show(); 錯誤:二義性
// 告訴編譯器用哪個基類的代碼
sb.Bed::show();
sb.Sofa::show();
// sb.Furniture::show(); 錯誤:二義性
return 0;
}
#include <iostream>
using namespace std;
class Furniture // 家具
{
public:
void show()
{
cout << "這是個家具" << endl;
}
};
class Bed:virtual public Furniture // 床
{
};
class Sofa:virtual public Furniture // 沙發
{
};
class SofaBed:public Bed,public Sofa // 沙發床
{
};
int main()
{
SofaBed sb;
sb.show();
return 0;
}
4.3.5權限的繼承
公有繼承:派生類可以繼承基類所有權限的成員,但是無法直接訪問基類的私有成員,對于基類的保護成員與公有成員,在派生類中仍然是原來的權限,
保護繼承:派生類可以繼承基類所有權限的成員,但是無法直接訪問基類的私有成員,對于基類的保護成員與公有成員,在派生類中都變為保護權限,
私有繼承:派生類可以繼承基類所有權限的成員,但是無法直接訪問基類的私有成員,對于基類的保護成員與公有成員,在派生類中都變為私有權限,
#include <iostream>
using namespace std;
class Father
{
private:
string str1 = "Father的私有權限";
protected:
string str2 = "Father的保護權限";
public:
string str3 = "Father的公有權限";
};
class Son:public Father
{
};
class Grandson:public Son
{
public:
void test()
{
cout << str2 << endl;
cout << str3 << endl;
}
};
int main()
{
Son s;
// cout << s.str2 << endl; 錯誤
cout << s.str3 << endl;
Grandson gs;
gs.test();
return 0;
}
#include <iostream>
using namespace std;
class Father
{
private:
string str1 = "Father的私有權限";
protected:
string str2 = "Father的保護權限";
public:
string str3 = "Father的公有權限";
};
class Son:protected Father
{
};
class Grandson:public Son
{
public:
void test()
{
cout << str2 << endl;
cout << str3 << endl;
}
};
int main()
{
Son s;
// cout << s.str2 << endl; 錯誤
// cout << s.str3 << endl; 錯誤
Grandson gs;
gs.test();
return 0;
}
#include <iostream>
using namespace std;
class Father
{
private:
string str1 = "Father的私有權限";
protected:
string str2 = "Father的保護權限";
public:
string str3 = "Father的公有權限";
};
class Son:private Father
{
public:
void test()
{
cout << str2 << endl;
cout << str3 << endl;
}
};
class Grandson:public Son
{
public:
void test()
{
// cout << str2 << endl; 錯誤
// cout << str3 << endl; 錯誤
}
};
int main()
{
Son s;
// cout << s.str2 << endl; 錯誤
// cout << s.str3 << endl; 錯誤
Grandson gs;
gs.test();
s.test();
return 0;
}
4.4多型的體現
多型可以理解為只寫一個函式介面,在程式運行時才決定呼叫型別對應的代碼,
多型的使用,需要使用以下條件:
- 必須使用公有繼承
- 派生類要有函式覆寫
- 基類參考/指標指向派生類物件
4.4.1函式覆寫
函式覆寫與函式隱藏很相似(函式隱藏就是重名),區別在與基類被覆寫的函式需要設定為虛函式,
虛函式有以下特點:
- 虛函式具有傳遞性,基類使用,派生類覆寫函式會自動生成虛函式
- 只有非靜態成員函式與解構式可以被定義為虛函式
- 如果宣告定義分離,只需要使用virtual關鍵字修飾函式宣告處
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void eat() // 虛函式
{
cout << "動物吃東西" << endl;
}
};
class Cat:public Animal
{
public:
void eat() // 虛函式
{
cout << "貓吃魚" << endl;
}
};
class Dog:public Animal
{
public:
void eat() // 虛函式
{
cout << "狗吃骨頭" << endl;
}
};
4.4.2使用多型
多型通常搭配函式引數使用,分別寫兩個函式,觸發參考和指標型別的多型
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void eat() // 虛函式
{
cout << "動物吃東西" << endl;
}
};
class Cat:public Animal
{
public:
void eat() // 虛函式
{
cout << "貓吃魚" << endl;
}
};
class Dog:public Animal
{
public:
void eat() // 虛函式
{
cout << "狗吃骨頭" << endl;
}
};
// 參考多型
void test_eat1(Animal& a)
{
a.eat();
}
void test_eat2(Animal* a)
{
a->eat();
}
int main()
{
Animal a1;
Cat c1;
Dog d1;
// 測驗參考多型效果
test_eat1(a1); // 動物吃東西
test_eat1(c1); // 貓吃魚
test_eat1(d1); // 狗吃骨頭
Animal* a2 = new Animal;
Cat* c2 = new Cat;
Dog* d2 = new Dog;
test_eat2(a2); // 動物吃東西
test_eat2(c2); // 貓吃魚
test_eat2(d2); // 狗吃骨頭
return 0;
}
4.4.2虛解構式
通過基類參考或指標指向派生類物件,當使用delete銷毀物件時,只會呼叫基類的解構式,不會呼叫派生類的解構式,此時,如果派生類中有new申請記憶體資源,那么會造成記憶體泄漏問題
#include <iostream>
using namespace std;
class Animal
{
public:
virtual ~Animal()
{
cout << "基類的解構式" << endl;
}
};
class Dog:public Animal
{
public:
~Dog()
{
cout << "派生類的解構式" << endl;
}
};
int main()
{
Animal* a = new Dog;
delete a; //派生類的解構式 基類的解構式
return 0;
}
因此在設計一個類時,如果這個類會成為其他類的基類,哪怕解構式什么都不寫,也要手寫空的解構式并加上virtual關鍵字修飾,除非可以確定此類不會被任何類繼承,
5.static關鍵字
5.1靜態成員變數
特點:
- 需要類內宣告,類外定義
- 所有物件共用一份,非靜態的物件各持有一份
- 可以直接類名呼叫,無需成員呼叫
- 運行時就開辟了,結束運行自動回收
- this可以呼叫靜態成員(靜態成員變數和靜態成員函式)
#include <iostream>
using namespace std;
class Test
{
public:
string str1 = "非靜態成員變數";
static string str2; // 類內只宣告
static const int a = 1; // 【特例】const修飾的靜態成員變數可以類內初始化
void function() // 非靜態的成員函式
{
// 為了方便理解,加入this指標,實際撰寫的程序中可取消
cout << this->str1 << endl;
cout << this->str2 << endl;
}
};
// 類外初始化
string Test::str2 = "靜態成員變數";
int main()
{
// 直接使用類名呼叫
cout << Test::str2 << " " << &Test::str2 << endl; // 靜態成員變數 0x40b038
Test t1;
cout << t1.str1 << " " << &t1.str1 << endl; // 非靜態成員變數 0x61fe8c
cout << t1.str2 << " " << &t1.str2<< endl; // 靜態成員變數 0x40b038
Test t2;
cout << t2.str1 << " " << &t2.str1<< endl; // 非靜態成員變數 0x61fe88
cout << t2.str2 << " " << &t2.str2<< endl; // 靜態成員變數 0x40b038
t1.function(); // 非靜態成員變數\n靜態成員變數
return 0;
}
5.2靜態成員函式
#include <iostream>
using namespace std;
class Test
{
private:
string str1 = "非靜態成員";
static string str2;
public:
void function1()
{
cout << "這是一個非靜態成員函式:";
cout << str1;
cout << str2;
cout << endl;
}
static void function2()
{
cout << "這是一個靜態成員函式:";
// cout << str1; 錯誤
cout << str2;
cout << endl;
}
};
string Test::str2 = "靜態成員";
int main()
{
Test::function2();
Test t;
t.function1();
t.function2(); // 也能通過物件,雖然不建議
return 0;
}
5.3靜態區域變數
第一次被呼叫的時候,就開辟空間,結束后,不會銷毀,下次被呼叫時,再次使用之前的靜態區域變數,直到程式運行終止才會自動銷毀
#include <iostream>
using namespace std;
class Test
{
public:
void func1()
{
int a = 1; // 非靜態區域變數
cout << a++ << endl;
}
void func2()
{
static int a = 1; // 靜態區域變數
cout << a++ << endl;
}
};
int main()
{
Test t1;
Test t2;
t1.func1(); // 1
t1.func1(); // 1
t2.func1(); // 1
cout << "---------------" << endl;
t1.func2(); // 1
t1.func2(); // 2
t2.func2(); // 3
return 0;
}
6.const關鍵字
6.1.修飾成員函式
修飾成員函式,稱為常成員函式,特點:無法修改屬性值,無法呼叫非const修飾的成員函式
#include <iostream>
using namespace std;
class Car
{
private:
string brand;
public:
Car(string brand):brand(brand){}
string get_brand() const
{
// set_brand("奔馳"); 錯誤
// brand = "寶馬"; 錯誤
// show(); 錯誤
show2();
return brand;
}
void set_brand(string brand)
{
this->brand = brand;
}
void show()
{
cout << "滴滴滴" << endl;
}
void show2() const
{
cout << "噠噠噠" << endl;
}
};
int main()
{
Car c("奧迪");
c.set_brand("大眾");
cout << c.get_brand() << endl;
return 0;
}
6.2.修飾物件
修飾物件,表示該物件是一個常量物件,常量物件屬性值不可以改,不能呼叫共非const成員函式
#include <iostream>
using namespace std;
class Car
{
private:
string brand;
public:
string model = "這個變數僅用于舉例,就不封裝了";
Car(string brand):brand(brand){}
string get_brand() const
{
// set_brand("奔馳"); 錯誤
// brand = "寶馬"; 錯誤
// show(); 錯誤
show2();
return brand;
}
void set_brand(string brand)
{
this->brand = brand;
}
void show()
{
cout << "滴滴滴" << endl;
}
void show2() const
{
cout << "噠噠噠" << endl;
}
};
int main()
{
// const兩個位置都可以
const Car c1("奧迪");
Car const c2("凱迪拉克");
// c1.set_brand("大眾"); 錯誤
cout << c1.get_brand() << endl;
// c1.show(); 錯誤
c1.show2();
// c1.model = "A4"; 錯誤
cout << c1.model << endl;
return 0;
}
6.3.修飾成員變數
修飾后,表示常成員變數,出生在設定后,就不能在運行期間發生變化
#include <iostream>
using namespace std;
class Person
{
private:
const string name; // 常成員變數
public:
const int age = 1; // 賦予初始值方式二
Person():name("張三"){} //賦予初始值方式一(推薦)
// 多載的建構式
Person(string name,int age):name(name),age(age){}
void set_name(string name)
{
// this->name = name; 錯誤
}
string get_name() const
{
return name;
}
};
int main()
{
Person p;
cout << p.get_name() << endl;
// p.age++; 錯誤
cout << p.age << endl;
Person p2("李四",18);
cout << p2.age << endl; // age:18
return 0;
}
6.4.修飾區域變數
表示區域變數不可變,通常用于參考型別的函式引數
#include <iostream>
using namespace std;
void show(const int& a,const int& b)
{
cout << a << b << endl;
}
int main()
{
int a = 1;
int b = 2;
show(a,b); // 12
return 0;
}
7.運算子多載
7.1友元
友元可以突破封裝性的權限限制,隨意訪問類中任意部分,可以分為:友元函式,友元類,友元成員函式
注意點:
- 運算子多載只能在C++已有的運算子范圍內,不能創建新的運算子,
- 運算子多載本質上也是函式多載,
- 多載之后的運算子無法改變原有的優先級和結合性,也不能改變運算子的運算元和語法結構
- 運算子多載的引數必須包含自定義資料型別,無法多載基本型別的運算子規則,
- 運算子多載盡量符合原功能定義,
- 運算子多載的引數不支持默認值的設定,
- 一般情況下,建議單目運算子使用成員函式多載,雙目運算子使用友元函式多載
7.1.1友元函式
特點:
- 不屬于任何一個類
- 沒有this指標,突破權限需要物件
- 友元函式的宣告可以放在類中任何位置,包括private
- 一個友元函式可以是多個類的友元函式,只需要各個類分別宣告,
#include
using namespace std;
class Job
{
private:
int income;
public:
Job(int income):income(income)
{
cout << &this->income << endl;
}
// “宣告”友元函式
friend void test_friend(Job&);
};
void test_friend(Job& j)
{
// 嘗試訪問私有成員
cout << ++j.income << " " << &j.income << endl;
}
int main()
{
Job j1(20000); // 0x61fe8c
test_friend(j1); // 20001 0x61fe8c
return 0;
}
7.1.2友元類
當一個類B成為另外一個類A的友元類的時候,類A的所有成員都可以被B類訪問
注意點:
- 友元關系與繼承無關
- 友元關系是單向的,不具有交換性
- 友元關系不具有傳遞性
#include <iostream>
using namespace std;
class A
{
private:
int value = https://www.cnblogs.com/moveddown/archive/2023/06/18/1;
public:
int get_value() const
{
return value;
}
// “宣告”友元關系
friend class B;
};
class B
{
public:
void test_friend(A& a)
{
cout << ++a.value << endl;
}
void test_friend2(A& a,int v)
{
a.value += v;
}
};
int main()
{
A a;
B b;
b.test_friend(a); // 2
cout << a.get_value() << endl; // 2
b.test_friend2(a,100);
cout << a.get_value() << endl; // 102
return 0;
}
7.1.3友元成員函式
#include <iostream>
using namespace std;
// 第三步:提前宣告類A
class A;
// 第二步:因為友元用到了類B,補充類B和函式宣告,
class B
{
public:
void func(A& a);
};
// 第一步:確定友元的函式格式并“宣告”
class A
{
private:
string str = "這是類A私有的成員!";
// 友元關系
friend void B::func(A& a);
};
// 第四步:定義友元成員函式的內容
void B::func(A &a)
{
cout << a.str.append("哈哈哈") << endl;
}
int main()
{
A a;
B b;
b.func(a); // 這是類A私有的成員!哈哈哈
return 0;
}
7.2.多載

可以把運算子看作是一個函式,給已有運算子賦新的功能,完成特定的操作,就需要運算子多載,運算子多載有2種多載方式,友元函式運算子多載,成員函式運算子多載
7.2.1友元函式運算子多載

#include <iostream>
using namespace std;
/**
* @brief 自定義整數型別
*/
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// “宣告”友元關系
friend Integer operator +(const Integer& i1,const Integer& i2);
friend Integer operator ++(Integer& i); // 前置
friend Integer operator ++(Integer& i,int); // 后置
};
Integer operator +(const Integer& i1,const Integer& i2)
{
return i1.value + i2.value;
}
Integer operator ++(Integer& i)
{
return ++i.value;
}
Integer operator ++(Integer& i,int)
{
return i.value++;
}
int main()
{
Integer i1(1);
cout << (++i1).get_value() << endl; // 2
Integer i2(2);
Integer i3 = i1+i2;
cout << (i3++).get_value() << endl; // 4
cout << i3.get_value() << endl; // 5
return 0;
}
7.2.2成員函式運算子多載
區別在于,是成員函式,第一個運算元使用this指標表示
注意:下面兩個必須使用成員函式多載
- 賦值運算子多載
- 型別轉換運算子多載

#include <iostream>
using namespace std;
/**
* @brief 自定義整數型別
*/
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 成員函式運算子多載
Integer operator +(const Integer& i); // 類內宣告
Integer operator ++(); // 前置++
Integer operator ++(int); // 后置++
};
// 類外定義
Integer Integer::operator +(const Integer& i)
{
return this->value+i.value;
}
Integer Integer::operator ++()
{
return ++this->value;
}
Integer Integer::operator ++(int)
{
return this->value++;
}
int main()
{
Integer i1(1);
cout << (++i1).get_value() << endl; // 2
Integer i2(2);
Integer i3 = i1+i2;
cout << (i3++).get_value() << endl; // 4
cout << i3.get_value() << endl; // 5
return 0;
}
#include <iostream>
using namespace std;
class City
{
private:
string name;
public:
City(string name):name(name){}
string get_name() const
{
return name;
}
// 以下代碼寫不寫都一樣
City& operator =(const City& c)
{
cout << "賦值運算子多載" << endl; // 默認無這行代碼
this->name = c.name;
return *this;
}
};
int main()
{
City c1("濟南");
City c2("青島");
// 賦值運算子
cout << (c2 = c1).get_name() << endl; // 濟南
return 0;
}
#include <iostream>
using namespace std;
class Test
{
public:
// 型別轉換運算子多載
operator int()
{
return 123;
}
};
int main()
{
Test t;
int a = t; // 型別轉換
cout << a << endl;
return 0;
}
8.模板
使函式或宣告為一種通用型別,這種編程方式稱為泛型編程,
8.1.函式模板
#include <iostream>
using namespace std;
template <class T> // 宣告模板的通用資料型別T
T add(T a,T b)
{
return a+b;
}
int main()
{
// 運行時決定具體型別的計算方式
cout << add(2,3) << endl; // 5
cout << add(2.2,3.3) << endl; // 5.5
string s1 = "AAA";
string s2 = "BBB";
cout << add(s1,s2) << endl; // AAABBB
// const char* 不支持加法
// cout << add("111","222") << endl; 錯誤
return 0;
}
8.2.類模板
#include <iostream>
using namespace std;
template <typename T> // typename可與class關鍵字替換
class Test
{
private:
T value;
public:
Test(T v):value(v){}
T get_value() const
{
return value;
}
};
class Projector // 投影儀
{
public:
void show()
{
cout << "投影儀播放內容中..." << endl;
}
};
int main()
{
Test<int> t1(123);
cout << t1.get_value() << endl; //123
Projector p;
Test<Projector> t2(p);
t2.get_value().show(); // 投影儀播放內容中...
return 0;
}
#include <iostream>
using namespace std;
template <typename T> // typename可與class關鍵字替換
class Test
{
private:
T value;
public:
Test(T v);
T get_value() const;
};
template <typename T>
Test<T>::Test(T v)
{
value = https://www.cnblogs.com/moveddown/archive/2023/06/18/v;
}
template
T Test::get_value() const
{
return value;
}
class Projector // 投影儀
{
public:
void show()
{
cout <<"投影儀播放內容中..." << endl;
}
};
int main()
{
cout << t1.get_value() << endl; //123
Projector p;
Test<Projector> t2(p);
t2.get_value().show(); // 投影儀播放內容中...
return 0;
}
9.容器
容器是用來存放資料元素的集合,容器分為順序容器,關聯容器
9.1順序容器
9.1.1array陣列
#include <iostream>
#include <array> // 容器類都需要引入頭檔案
using namespace std;
int main()
{
// 創建一個長度為5的陣列物件
array<int,5> arr = {1,2,3};
// 前三個元素的值是1,2,3
cout << arr[0] << endl; // 1
// 默認值0(不同的編譯環境可能有所區別)
cout << arr[3] << endl; // 0
// 修改第四個元素的值
arr[3] = 888;
cout << arr[3] << endl; // 888
// 也支持at函式(推薦)
cout << arr.at(3) << endl; // 888
cout << "------普通for回圈-------" << endl;
for(int i = 0;i<arr.size();i++)
{
cout << arr.at(i) << " ";
}
cout << endl;
cout << "------for each回圈-------" << endl;
for(int i:arr)
{
cout << i << " ";
}
cout << endl;
cout << "------迭代器------" << endl;
arr<int,5>::iterator iter;//iterator 迭代器型別為讀寫,只是讀為const_iterator
for(iter=arr.begin();iter!=arr.end();iter++)
{
cout <<*iter << " ";
}
return 0;
}
9.1.2vector向量
#include <iostream>
#include <vector> // 容器類都需要引入頭檔案
using namespace std;
int main()
{
// 創建初始元素為5的向量物件
vector<int> vec(5);
cout << vec.size() << endl; // 5
// 可以使用[]或at函式取出元素,推薦后者
cout << vec.at(0) << endl; // 0
// 判斷是否為空
cout << vec.empty() << endl; // 0
// 尾部追加
vec.push_back(8);
cout << vec.at(vec.size()-1) << endl; // 8
// 在第一個位置插入一個元素
// 引數1:插入位置,begin函式回傳一個迭代器指標,指向第一個元素
// 引數2:插入內容
vec.insert(vec.begin(),1);
// 在倒數第二個位置插入一個元素
// 引數1:end函式回傳一個迭代器指標,指向最后一個元素的后面
// 引數2:插入內容
vec.insert(vec.end()-1,678);
// 修改第二個元素
vec[1] = 2;
// 洗掉第二個元素
vec.erase(vec.begin()+1);
// 洗掉倒數第二個元素
vec.erase(vec.end()-2);
cout << "------普通for回圈-------" << endl;
for(int i = 0;i<vec.size();i++)
{
cout << vec.at(i) << " ";
}
cout << endl;
vec.clear(); // 清空
cout << "------for each回圈-------" << endl;
for(int i:vec)
{
cout << i << " ";
}
cout << endl;
cout << "------迭代器------" << endl;
vector<int>::const_iterator iter;//iterator 迭代器型別為讀寫,只是讀為const_iterator
for(iter=vec.begin();iter!=vec.end();iter++)
{
cout <<*iter << " ";
}
return 0;
}
9.1.3list串列
#include <iostream>
#include <list> // 容器類都需要引入頭檔案
using namespace std;
int main()
{
// 創建一個元素是4個hello的串列物件
list<string> lis(4,"hello");
// 判斷是否為空
cout << lis.empty() << endl; // 0
// 向后追加元素
lis.push_back("bye");
// 頭插
lis.push_front("hi");
// 在第二個位置插入元素“second”
// 注意:迭代器指標不支持+運算,支持++運算
lis.insert(++lis.begin(),"second");
// 在倒數第二個位置插入元素"aaa"
// 注意:迭代器指標不支持-運算,支持--運算
lis.insert(--lis.end(),"aaa");
// 到第5個位置插入元素“555”
// 1. 先拿到第一個元素位置的迭代器指標
list<string>::iterator iter = lis.begin();
// 2. 向后移動4位
// 引數1:迭代器指標
// 引數2:向后移動的數量,負數為向前
advance(iter,4);
// 3. 插入元素“555”
lis.insert(iter,"555");
// 修改第五個元素為“666”,【重新獲取】并移動迭代器指標
iter = lis.begin();
advance(iter,4);
*iter = "666";
// 洗掉第一個元素
lis.pop_front();
// 洗掉最后一個元素
lis.pop_back();
// 洗掉第四個元素
iter = lis.begin();
advance(iter,3);
lis.erase(iter);
// 取出第一個和最后一個元素
cout << lis.front() << " " << lis.back() << endl;
// 不支持普通for回圈遍歷
cout << "------for each回圈-------" << endl;
for(string i:lis)
{
cout << i << " ";
}
cout << endl;
cout << "------迭代器------" << endl;
list<string>::const_iterator iter;//iterator 迭代器型別為讀寫,只是讀為const_iterator
for(iter=lis.begin();iter!=lis.end();iter++)
{
cout <<*iter << " ";
}
return 0;
}
9.1.4deque佇列
從API上兼容vector和list,從性能上位于vector和list之間
9.2.關聯容器
9.2.1map鍵值對
元素以鍵值對的方式存盤,鍵必須具有唯一性,值可以重復;鍵通常是字串型別,而值可以是任何型別,
#include <iostream>
#include <map> // 容器類都需要引入頭檔案
using namespace std;
int main()
{
// 創建一個map物件,尖括號中分別為鍵與值的型別
map<string,int> map1;
// 插入資料
map1["height"] = 177;
map1["weight"] = 80;
map1["age"] = 25;
map1.insert(pair<string,int>("salary",12000));
// 取出元素
cout << map1["salary"] << endl; // 12000
// 更推薦使用at函式
cout << map1.at("age") << endl; // 25
// 修改元素
map1["weight"] = 70;
cout << map1["weight"] << endl;
// 洗掉元素
map1.erase("height");
// 判斷"身高"鍵值存不存在
if(map1.find("age") != map1.end()) // 在
{
cout << "鍵值對存在!" << endl;
}else // 不在
{
cout << "鍵值對不存在!" << endl;
}
cout << map1.size() << endl;
// 清空
map1.clear();
cout << map1.size() << endl; // 0
cout << "------迭代器------" << endl;
map<string,int>::const_iterator iter;
for(iter=map1.begin();iter!=map1.end();iter++)
{
// 鍵使用first表示
// 值使用second表示
cout << iter->first << " " << iter->second << endl;
}
cout << "主函式直接完畢" << endl;
return 0;
}
10.抽象類
如果我們的基類只表達一些抽象的概念,并不與具體的物件相聯系,他可以為派生類提供一個框架,這就是抽象類,
如果一個類有至少一個純虛函式,則這個類是抽象類,
如果一個類時抽象類,則這個類至少也有一個純虛函式,
純虛函式是一種特殊的虛函式,純虛函式只有宣告,沒有定義,
抽象類的解構式都應該寫為虛解構式,
#include <iostream>
using namespace std;
class Shape
{
public:
//純虛函式
virtual void perimeter() = 0;
virtual void area() = 0;
virtual ~Shape(){}
};
int main()
{
// Shape s; //錯誤
return 0;
}
10.1抽象類的使用
使用派生類實體化所有虛函式,此時,所有的派生類都會變成一個普通的類,就可以實體化了,
#include <iostream>
using namespace std;
//形狀類
class Shape
{
public:
//純虛函式
virtual void perimeter() = 0;
virtual void area() = 0;
virtual ~Shape()
{
}
};
//圓形類
class Circle:public Shape
{
public:
void perimeter()
{
cout << "周長:2πR" << endl;
}
void area()
{
cout << "面積:πR^2" << endl;
}
};
int main()
{
Circle c;
c.perimeter();
c.area();
return 0;
}
如果沒有實體化完,還需要再實體化完為止
#include <iostream>
using namespace std;
class Shape
{
public:
//純虛函式
virtual void perimeter() = 0;
virtual void area() = 0;
virtual ~Shape()
{
}
};
//多邊形類
class Polygon:public Shape
{
public:
void perimeter()
{
cout << "周長:∑邊長" << endl;
}
};
//矩形類
class Rectangle:public Polygon
{
public:
void area()
{
cout << "面積" << endl;
}
};
int main()
{
// Polygon pg; 還是抽象類,只實作了部分純虛函式
Rectangle ra;
ra.perimeter();
ra.area();
return 0;
}
11.字串類
#include <iostream>
using namespace std;
int main()
{
//創建一個內容為空的字串物件
string s;
//判斷字串是否為空:empty()
cout << s.empty() << endl; //1
string s1 = "Monday"; //隱式呼叫建構式
string s2("Monady"); //顯示呼叫建構式
cout << (s1==s2) << endl; //0
string s3(s2); //拷貝建構式
s2 = s1; //賦值運算子
cout << s3 << " " << s2 << endl; //Monady Monday
//引數1:char*原字串 引數2:從前往后保留字符數
string s4("ASDFF",2);
cout << s4 << endl; //AS
//引數1:string原字串 引數2:從前往后不保留的一個字符數
string s5(s2,2);
cout << s5 << endl; //nday
//引數1:字符數量 引數2:char字符記憶體
string s6(5,'A');
cout << s6 << endl; //AAAAA
//交換字串--swap()
swap(s5,s6);
cout << s5 << " " << s6 << endl; //AAAAA nday
//字串連接:+
string s7 = s4 + s6;
cout << s7 << endl; //ASnday
//向后追加字串:append()
s7.append("YYY").append("WUW");
cout << s7 << endl; //ASndayYYYWUW
//向后追加單字符:push_back()
s7.push_back('p');
cout << s7 << endl; //ASndayYYYWUWp
//插入字串:insert() 引數1:插入的位置 引數2:插入的內容
s7.insert(2,"******");
cout << s7 << endl; //AS******ndayYYYWUWp
//洗掉:erase() 引數1:洗掉的起始位置 引數2:洗掉的字符數量
s7.erase(4,3);
cout << s7 << endl; //AS***ndayYYYWUWp
//替換:replace() 引數1:替換的起始位置 引數2:替換的字符數量 引數3:替換的新內容
s7.replace(0,3,"====");
cout << s7 << endl; //====**ndayYYYWUWp
//清空:clear()
s7.clear();
cout << s7.size() << endl; //0
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555504.html
標籤:其他
上一篇:Java Websocket 01: 原生模式 Websocket 基礎通信
下一篇:返回列表
