- 函數提高
3.1 函數默認參數
函數的形參可以有默認值,調用時可以省略這些參數。
示例代碼:
int func(int a, int b = 10, int c = 10) {return a + b + c;
}int main() {cout << "ret = " << func(20, 20) << endl;cout << "ret = " << func(100) << endl;system("pause");return 0;
}
3.2 函數占位參數
占位參數在函數聲明中占位,調用時必須提供值。
示例代碼:
void func(int a, int) {cout << "this is func" << endl;
}int main() {func(10, 10); // 占位參數必須提供值system("pause");return 0;
}
3.3 函數重載
函數重載允許函數名相同,但參數類型、個數或順序不同。
示例代碼:
void func() {cout << "func 的調用!" << endl;
}void func(int a) {cout << "func (int a) 的調用!" << endl;
}void func(double a) {cout << "func (double a)的調用!" << endl;
}void func(int a, double b) {cout << "func (int a ,double b) 的調用!" << endl;
}int main() {
func();
func(10);
func(3.14);
func(10, 3.14);
func(3.14, 10);
system("pause");
return 0;
}
void func(int& a)
{cout << "func(int &a)調用" << endl;
}void func(const int& a)
{cout << "func(const int &a)調用" << endl;
}void func2(int a, int b = 10)
{cout << "func2(int a , int b = 10)調用" << endl;
}void func2(int a)
{cout << "func2(int a)調用" << endl;
}
int main()
{int a = 10;func(a);func(10);//func2(10);system("pause");return 0;
}
4 類和對象
面向對象的三大特性是封裝、繼承和多態。類是對象的抽象,對象是類的實例。
4.1 封裝
封裝是將屬性和行為作為一個整體,表現生活中的事物,并加以權限控制。
4.1.1 封裝的意義
示例代碼:
// 圓類
const double PI = 3.14;
class Circle {
public:int m_r; // 半徑double calculateZC() { // 計算周長return 2 * PI * m_r;}
};int main() {Circle c1;c1.m_r = 10;cout << "圓的周長為: " << c1.calculateZC() << endl;system("pause");return 0;
}
案例2:
#include <string>
class Student
{
public:void setName(string name){m_name = name;}void setID(int id){m_id = id;}void showStudent(){cout << "name: " << m_name << "id: " << m_id << endl;}
public:string m_name;int m_id;
};
int main()
{Student stu;stu.setName("德瑪西亞");stu.setID(250);stu.showStudent();system("pause");return 0;
}
4.1.2 struct 和 class 的區別
struct 默認權限為公共。
class 默認權限為私有。
示例代碼:
class c1 //默認私有
{int m_A;};
struct c2 //默認公共
{int m_A;};
int main()
{c1 c1;c1.m_A = 10;//errc2 c2;c2.m_A = 10;system("pause");return 0;
}
4.1.3 成員屬性設置為私有
將成員屬性設置為私有可以控制讀寫權限,并檢測數據有效性。
示例代碼:
class Person {
public:void setName(string name) {m_Name = name;}string getName() {return m_Name;}int getAge() {return m_Age;}void setAge(int age) {if (age < 0 || age > 150) {cout << "你個老妖精!" << endl;return;}m_Age = age;}void setLover(string lover) {m_Lover = lover;}
private:string m_Name; // 姓名int m_Age; // 年齡string m_Lover; // 情人
};int main() {Person p;p.setName("張三");cout << "姓名: " << p.getName() << endl;p.setAge(50);cout << "年齡: " << p.getAge() << endl;p.setLover("蒼井");system("pause");return 0;
}
4.2 對象的初始化和清理
構造函數和析構函數是C++中用于對象初始化和清理的重要機制。
4.2.1 構造函數和析構函數
構造函數用于對象創建時的初始化,析構函數用于對象銷毀前的清理。
示例代碼:
class Person {
public:Person() {cout << "Person的構造函數調用" << endl;}~Person() {cout << "Person的析構函數調用" << endl;}
};void test01() {Person p;
}int main() {test01();system("pause");return 0;
}
4.2.2 構造函數的分類及調用
構造函數可以分為有參構造和無參構造,還可以分為普通構造和拷貝構造。
示例代碼:
class Person {
public:Person() {cout << "無參構造函數!" << endl;}Person(int a) {age = a;cout << "有參構造函數!" << endl;}Person(const Person& p) {age = p.age;cout << "拷貝構造函數!" << endl;}~Person() {cout << "析構函數!" << endl;}
public:int age;
};void test01() {Person p; // 調用無參構造函數
}void test02() {Person p1(10); // 調用有參構造函數Person p2 = Person(10); // 調用拷貝構造函數Person p3 = p1; // 調用拷貝構造函數
}int main() {test01();test02();system("pause");return 0;
}
4.2.3 拷貝構造函數調用時機
拷貝構造函數在以下三種情況下被調用:
使用一個已經創建完畢的對象來初始化一個新對象。
以值傳遞的方式給函數參數傳值。
以值方式返回局部對象。
示例代碼:
class Person
{
public:Person(){cout << "無參構造函數" << endl;}Person(int a){age = a;cout << "有參構造函數" << endl;}Person(const Person& p){cout << "拷貝構造函數" << endl;age = p.age;}~Person(){cout << "析構函數" << endl;}
public:int age;
};void test01()
{Person p;
}void test02()
{Person p1(10);Person p2 = Person(10);Person p3 = Person(p2);Person p4 = 10;Person p5 = p4;
}
int main()
{test01();test02();system("pause");return 0;
}
class Person
{
public:Person(){cout << "無參構造函數" << endl;mAge = 0;}Person(int age){cout << "有參構造函數" << endl;}Person(const Person& p){cout << "拷貝構造函數" << endl;mAge = p.mAge;}~Person(){cout << "析構函數" << endl;}
public:int mAge;
};void test01()
{Person man(100);Person newman(man);Person newman2 = man;
}void dowork(Person p1) {};
void test02()
{Person p;dowork(p);
}Person dowork2()
{Person p1;cout << (int*)&p1 << endl;return p1;
}void test03()
{Person p = dowork2();cout << (int*)&p << endl;
}
int main()
{//test01();//test02();test03();system("pause");return 0;
}
4.2.4 構造函數調用規則
如果用戶定義了有參構造函數,編譯器不會再提供默認無參構造函數,但會提供默認拷貝構造函數。
示例代碼:
class Person {
public:Person() {cout << "無參構造函數!" << endl;}Person(int a) {age = a;cout << "有參構造函數!" << endl;}Person(const Person& p) {age = p.age;cout << "拷貝構造函數!" << endl;}~Person() {cout << "析構函數!" << endl;}
public:int age;
};void test01() {Person p1(18);Person p2(p1); // 調用拷貝構造函數cout << "p2的年齡為: " << p2.age << endl;
}void test02() {Person p1; // 如果用戶沒有提供默認構造函數,會報錯Person p2(10); // 用戶提供的有參構造函數
}int main() {test01();test02();system("pause");return 0;
}
4.2.5 深拷貝與淺拷貝
淺拷貝只是簡單地賦值,而深拷貝會在堆區重新申請空間。
示例代碼:
class Person {
public:Person() {cout << "無參構造函數!" << endl;}Person(int age, int height) {cout << "有參構造函數!" << endl;m_age = age;m_height = new int(height);}Person(const Person& p) {cout << "拷貝構造函數!" << endl;m_age = p.m_age;m_height = new int(*p.m_height); // 深拷貝}~Person() {cout << "析構函數!" << endl;if (m_height != NULL) {delete m_height;}}
public:int m_age;int* m_height;
};void test01() {Person p1(18, 180);Person p2(p1);cout << "p1的年齡: " << p1.m_age << " 身高: " << *p1.m_height << endl;cout << "p2的年齡: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}int main() {test01();system("pause");return 0;
4.2.6 初始化列表
初始化列表用于在構造函數中初始化成員變量。
示例代碼:
class Person {
public:Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c) {cout << "構造函數調用" << endl;}void PrintPerson() {cout << "mA:" << m_A << endl;cout << "mB:" << m_B << endl;cout << "mC:" << m_C << endl;}
private:int m_A;int m_B;int m_C;
};int main() {Person p(1, 2, 3);p.PrintPerson();system("pause");return 0;
}
4.2.7 類對象作為類成員
類中的成員可以是另一個類的對象。
示例代碼:
class Phone {
public:Phone(string name) {m_PhoneName = name;cout << "Phone構造" << endl;}~Phone() {cout << "Phone析構" << endl;}string m_PhoneName;
};class Person {
public:Person(string name, string pName) : m_Name(name), m_Phone(pName) {cout << "Person構造" << endl;}~Person() {cout << "Person析構" << endl;}void playGame() {cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手機! " << endl;}string m_Name;Phone m_Phone;
};void test01() {Person p("張三", "蘋果X");p.playGame();
}int main() {test01();system("pause");return 0;
}
4.2.8 靜態成員
靜態成員變量和靜態成員函數是類的所有對象共享的。
示例代碼:
class Person {
public:static int m_A; // 靜態成員變量static void func() {cout << "func調用" << endl;m_A = 100;}int m_B; // 非靜態成員變量
};int Person::m_A = 10;void test01() {Person p1;p1.func();Person::func();cout << "p1.m_A = " << p1.m_A << endl;cout << "m_A = " << Person::m_A << endl;
}int main() {test01();system("pause");return 0;
}
class Person
{
public:static int m_A;
private:static int m_B;};
int Person::m_A = 10;
int Person::m_B = 10;void test01()
{Person p1;p1.m_A = 100;cout << "p1.m_A = " << p1.m_A << endl;Person p2;p2.m_A = 200;cout << "p1.m_A = " << p1.m_A << endl;cout << "p2.m_A = " << p2.m_A << endl;cout << "m_A = " << Person::m_A << endl;}
int main()
{test01();system("pause");return 0;
}
4.3 C++對象模型和this指針
成員變量和成員函數分開存儲
this指針指向被調用的成員函數所屬的對象。
示例代碼:
class Person {
public:Person(int age) {this->age = age;}Person& PersonAddPerson(Person p) {this->age += p.age;return *this;}int age;
};void test01() {Person p1(10);cout << "p1.age = " << p1.age << endl;Person p2(10);p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);cout << "p2.age = " << p2.age << endl;
}int main() {test01();system("pause");return 0;
}
空指針訪問成員數組
4.4 友元
友元可以訪問類的私有成員。
4.4.1 全局函數做友元
示例代碼:
class Building {friend void goodGay(Building *building);string m_BedRoom; // 臥室
public:Building() {m_BedRoom = "臥室";}string m_SittingRoom; // 客廳
};void goodGay(Building *building) {cout << "好基友正在訪問: " << building->m_SittingRoom << endl;cout << "好基友正在訪問: " << building->m_BedRoom << endl;
}void test01() {Building b;goodGay(&b);
}int main() {test01();system("pause");return 0;
}
4.4.2 類做友元
示例代碼:
class Building {friend class goodGay;string m_BedRoom; // 臥室
public:Building() {m_BedRoom = "臥室";}string m_SittingRoom; // 客廳
};class goodGay {
public:void visit(Building *building) {cout << "好基友正在訪問" << building->m_SittingRoom << endl;cout << "好基友正在訪問" << building->m_BedRoom << endl;}
};void test01() {Building b;goodGay gg;gg.visit(&b);
}int main() {test01();system("pause");return 0;
}
4.4.3 成員函數做友元
示例代碼:
class Building {friend void goodGay::visit(Building *building);string m_BedRoom; // 臥室
public:Building() {m_BedRoom = "臥室";}string m_SittingRoom; // 客廳
};class goodGay {
public:void visit(Building *building) {cout << "好基友正在訪問" << building->m_SittingRoom << endl;cout << "好基友正在訪問" << building->m_BedRoom << endl;}
};void test01() {Building b;goodGay gg;gg.visit(&b);
}int main() {test01();system("pause");return 0;
}
4.5 運算符重載
運算符重載可以為自定義數據類型重新定義運算符的行為。
4.5.1 加號運算符重載
示例代碼:
class Person
{
public:Person() {};Person(int a, int b){this->m_A = a;this->m_B = b;}Person operator+(const Person& p){Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}
public:int m_A;int m_B;};Person operator+(const Person& p2, int val)
{Person temp;temp.m_A = p2.m_A + val;temp.m_B = p2.m_B + val;return temp;
}void test()
{Person p1(10, 10);Person p2(20, 20);Person p3 = p2 + p1;cout << "mA:" << p3.m_A << "mB:" << p3.m_B << endl;Person p4 = p3 + 10;cout << "mA:" << p4.m_A << "mB:" << p4.m_B << endl;}
int main()
{test();system("pause");return 0;
}
4.5.2 左移運算符重載
示例代碼:
class Person {friend ostream& operator<<(ostream& out, Person& p);
public:Person(int a, int b) {this->m_A = a;this->m_B = b;}
private:int m_A;int m_B;
};ostream& operator<<(ostream& out, Person& p) {out << "a:" << p.m_A << " b:" << p.m_B;return out;
}void test() {Person p1(10, 20);cout << p1 << "hello world" << endl;
}int main() {test();system("pause");return 0;
}
4.5.3 遞增運算符重載
示例代碼:
class MyInteger {friend ostream& operator<<(ostream& out, MyInteger myint);
public:MyInteger() {m_Num = 0;}MyInteger& operator++() {m_Num++;return *this;}MyInteger operator++(int) {MyInteger temp = *this;m_Num++;return temp;}
private:int m_Num;
};ostream& operator<<(ostream& out, MyInteger myint) {out << myint.m_Num;return out;
}void test01() {MyInteger myInt;cout << ++myInt << endl;cout << myInt << endl;
}void test02() {MyInteger myInt;cout << myInt++ << endl;cout << myInt << endl;
}int main() {test01();test02();system("pause");return 0;
}
4.5.4 賦值運算符重載
示例代碼:
class Person {
public:Person(int age) {m_Age = new int(age);}Person& operator=(Person &p) {if (m_Age !=NULL) {
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age);
return *this;
}
~Person() {
if (m_Age != NULL) {
delete m_Age;
m_Age = NULL;
}
}
int *m_Age;
};
void test01() {
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;
cout << "p1的年齡為:" << *p1.m_Age << endl;
cout << "p2的年齡為:" << *p2.m_Age << endl;
cout << "p3的年齡為:" << *p3.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
4.5.5 關系運算符重載
示例代碼:
class Person {
public:Person(string name, int age) {this->m_Name = name;this->m_Age = age;}bool operator==(Person & p) {if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;} else {return false;}}bool operator!=(Person & p) {if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return false;} else {return true;}}string m_Name;int m_Age;
};void test01() {Person a("孫悟空", 18);Person b("孫悟空", 18);if (a == b) {cout << "a和b相等" << endl;} else {cout << "a和b不相等" << endl;}if (a != b) {cout << "a和b不相等" << endl;} else {cout << "a和b相等" << endl;}
}int main() {test01();system("pause");return 0;
}
4.5.6 函數調用運算符重載
示例代碼:
class MyPrint {
public:void operator()(string text) {cout << text << endl;}
};class MyAdd {
public:int operator()(int v1, int v2) {return v1 + v2;}
};void test01() {MyPrint myFunc;myFunc("hello world");
}void test02() {MyAdd add;int ret = add(10, 10);cout << "ret = " << ret << endl;cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}int main() {test01();test02();system("pause");return 0;
}
5. 繼承
繼承是面向對象的三大特性之一,允許子類繼承父類的成員。
5.1 繼承的基本語法
示例代碼:
class BasePage {
public:void header() {cout << "首頁、公開課、登錄、注冊...(公共頭部)" << endl;}void footer() {cout << "幫助中心、交流合作、站內地圖...(公共底部)" << endl;}void left() {cout << "Java,Python,C++...(公共分類列表)" << endl;}
};class Java : public BasePage {
public:void content() {cout << "JAVA學科視頻" << endl;}
};class Python : public BasePage {
public:void content() {cout << "Python學科視頻" << endl;}
};class CPP : public BasePage {
public:void content() {cout << "C++學科視頻" << endl;}
};void test01() {cout << "Java下載視頻頁面如下: " << endl;Java ja;ja.header();ja.footer();ja.left();ja.content();cout << "--------------------" << endl;cout << "Python下載視頻頁面如下: " << endl;Python py;py.header();py.footer();py.left();py.content();cout << "--------------------" << endl;cout << "C++下載視頻頁面如下: " << endl;CPP cp;cp.header();cp.footer();cp.left();cp.content();
}int main() {test01();system("pause");return 0;
}
5.2 繼承方式
繼承方式有三種:公共繼承、保護繼承和私有繼承。
示例代碼:
class Base1 {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : public Base1 {
public:void func() {m_A; // 可訪問 public 權限m_B; // 可訪問 protected 權限// m_C; // 不可訪問}
};class Base2 {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son2 : protected Base2 {
public:void func() {m_A; // 可訪問 protected 權限m_B; // 可訪問 protected 權限// m_C; // 不可訪問}
};class Base3 {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son3 : private Base3 {
public:void func() {m_A; // 可訪問 private 權限m_B; // 可訪問 private 權限// m_C; // 不可訪問}
};class GrandSon3 : public Son3 {
public:void func() {// Son3 是私有繼承,所以繼承 Son3 的屬性在 GrandSon3 中都無法訪問到// m_A;// m_B;// m_C;}
};
5.3 繼承中的對象模型
父類中的私有成員也會被子類繼承,但無法直接訪問。
示例代碼:
class Base {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son : public Base {
public:int m_D;
};void test01() {cout << "sizeof Son = " << sizeof(Son) << endl;
}int main() {test01();system("pause");return 0;
}
5.4 繼承中構造和析構順序
創建子類對象時,先調用父類的構造函數,再調用子類的構造函數。析構順序相反。
示例代碼:
class Base {
public:Base() {cout << "Base構造函數!" << endl;}~Base() {cout << "Base析構函數!" << endl;}
};class Son : public Base {
public:Son() {cout << "Son構造函數!" << endl;}~Son() {cout << "Son析構函數!" << endl;}
};void test01() {Son s;
}int main() {test01();system("pause");return 0;
}
5.5 繼承同名成員處理方式
子類與父類出現同名成員時,可以通過作用域區分。
示例代碼:
class Base {
public:void func() {cout << "Base - func()調用" << endl;}void func(int a) {cout << "Base - func(int a)調用" << endl;}int m_A;
};class Son : public Base {
public:void func() {cout << "Son - func()調用" << endl;}int m_A;
};void test01() {Son s;cout << "Son下的m_A = " << s.m_A << endl;cout << "Base下的m_A = " << s.Base::m_A << endl;s.func();s.Base::func();s.Base::func(10);
}int main() {test01();system("pause");return 0;
}
5.6 繼承同名靜態成員處理方式
靜態成員同名處理方式與非靜態成員相同。
示例代碼:
class Base {
public:static void func() {cout << "Base - static void func()" << endl;}static void func(int a) {cout << "Base - static void func(int a)" << endl;}static int m_A;
};int Base::m_A = 100;class Son : public Base {
public:
static void func() {
cout << "Son - static void func()" << endl;
}
static int m_A;
};
int Son::m_A = 200;
void test01() {
cout << "通過對象訪問: " << endl;
Son s;
cout << "Son 下 m_A = " << s.m_A << endl;
cout << "Base 下 m_A = " << s.Base::m_A << endl;
cout << "通過類名訪問: " << endl;
cout << "Son 下 m_A = " << Son::m_A << endl;
cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
void test02() {
cout << "通過對象訪問: " << endl;
Son s;
s.func();
s.Base::func();
cout << "通過類名訪問: " << endl;
Son::func();
Son::Base::func();
Son::Base::func(100);
}
int main() {
test01();
test02();
system("pause");
return 0;
}
5.7 多繼承語法
C++允許一個類繼承多個類。
示例代碼:
class Base1 {
public:Base1() {m_A = 100;}int m_A;
};class Base2 {
public:Base2() {m_A = 200;}int m_A;
};class Son : public Base2, public Base1 {
public:Son() {m_C = 300;m_D = 400;}int m_C;int m_D;
};void test01() {Son s;cout << "sizeof Son = " << sizeof(s) << endl;cout << s.Base1::m_A << endl;cout << s.Base2::m_A << endl;
}int main() {test01();system("pause");return 0;
}
5.8 菱形繼承
菱形繼承是指兩個派生類繼承同一個基類,而另一個類又同時繼承這兩個派生類。
示例代碼:
class Animal {
public:int m_Age;
};class Sheep : virtual public Animal {};class Tuo : virtual public Animal {};class SheepTuo : public Sheep, public Tuo {};void test01() {SheepTuo st;st.Sheep::m_Age = 100;st.Tuo::m_Age = 200;cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;cout << "st.m_Age = " << st.m_Age << endl;
}int main() {test01();system("pause");return 0;
}