複習
0.1 建構函式
物件被建立的時候,會自動呼叫
全域性物件
區域性物件
堆中的物件
建構函式的函式名字和類名一樣
建構函式沒有返回值,可以有引數,可以過載,一個類中可以實現多個建構函式
0.2 解構函式
物件被銷燬的時候,會自動呼叫
全域性物件 程式結束的時候會自動銷燬
區域性物件 離開作用域的時候,會自動銷燬
堆中的物件 delete釋放的時候,會自動銷燬
解構函式名字:~ 類名
析構沒有返回值,沒有引數,不能過載,一個類就只能有1個
0.3 複製建構函式
一個物件初始化另外一個物件的時候,會自動呼叫
class CTest
{
//......
private:
int m_nNum;
}
1. 直接初始化
int main()
{
CTest obj1;
CTest obj2 = obj1;
}
2. 函式傳參的時候
void Fun(CTest obj)
{
}
Fun(obj1);
編譯器預設會提供一個淺複製的複製構造。
當類中成員,有指標指向堆空間的時候,我們大機率需要自己實現一個深複製的複製構造
0.4 轉換建構函式
只有一個引數的建構函式,也稱之為轉換建構函式。
這個語法容易造成誤解,可以使用explicit 禁止轉換
1. 繼承
1.1 繼承的基本語法
#include <iostream>
class CA
{
public:
CA() :m_a(1)
{
}
void Fun1()
{
std::cout << m_a;
}
public:
int m_a;
};
//繼承的語法:
//class 子類:public 父類的名字
class CB:public CA
{
public:
CB() :m_b(2)
{
}
void Fun2()
{
std::cout << m_b;
}
public:
int m_b;
};
int main()
{
std::cout << sizeof(CB)<<std::endl;
CB objB;
objB.Fun1();//這個函式從CA繼承而來
objB.Fun2();//自己實現的
objB.m_a = 100;//使用的從CA繼承來的
objB.m_b = 200;//自己定義的
return 0;
}
1.3 繼承的許可權
子類包含父類中的所有成員
子類也可以使用父類中的成員
在使用類的成員的時候,有兩種情況:
1.透過類物件,在類外使用成員變數和成員函式
2.在類內的函式中,直接使用成員變數和成員函式
#include <iostream>
class CBase
{
public:
int m_BaseA;
void FunCBase()
{
std::cout << "我是CBase的FunCBase" << std::endl;
}
protected:
int m_BaseB;
private:
int m_BaseC;
};
class CA :public CBase
{
public:
void FunCA()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
}
};
class CB :protected CBase
{
public:
void FunCB()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
}
};
class CC :private CBase
{
public:
void FunCC()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
}
};
class CTest1 :public CB
{
public:
void FunTest1()
{
m_BaseA = 10;
m_BaseB = 20;
}
};
class CTest2 :public CC
{
public:
void FunTest2()
{
//m_BaseA = 10;
//m_BaseB = 20;
}
};
int main()
{
CA objA;
CB objB;
CC objC;
//CA是公有繼承,公有繼承也稱之為 【介面繼承】
//父類中是公有的,在子類中還是公有的,介面依然是介面
objA.m_BaseA = 1;
objA.FunCBase();
//私有繼承或者保護結成也稱之為 【實現繼承】
//會使得父類中的公有成員變為私有的或者保護的
//子類就失去了父類中的介面,變成了內部實現。
//objB.m_BaseA = 2;
//objB.FunCBase();
//objC.m_BaseA = 2;
//objC.FunCBase();
//保護繼承和私有繼承,都是實現繼承,有什麼區別呢???
//私有繼承,原公有或者保護成員,
//在子類中都變成了私有成員,再往下繼承,就會不可訪問
//保護繼承,原公有或者保護成員,
//在子類中都是保護屬性,再往下繼承,類內還是能訪問的
return 0;
}
1.4 重定義問題
父類中和子類中,只要重名了,就會發生重定義。透過子類物件訪問同名成員,預設訪問的都是子類自己的。父類中的同名成員被隱藏了。
無論是函式名和函式名同名,還是變數名和變數名同名,還是變數名和函式名同名。函式名同名,無論引數是一樣還是不一樣,都是重定義。父類中的識別符號,都會隱藏,預設使用子類自己的。
注意:父類和子類中同名函式,不能構成過載的,因為他們作用域不同
#include <iostream>
class CBase
{
public:
void Fun()
{
std::cout << "CBase的Fun" << std::endl;
}
void Fun1()
{
}
int m_BaseA;
};
class CA :public CBase
{
public:
void Fun1(int n)
{
}
int m_BaseA;
int Fun;
};
int main()
{
CA obj;
obj.Fun = 100;
obj.Fun();
obj.Fun1();
obj.m_BaseA = 10;
return 0;
}
1.5 在繼承關係中構造析構呼叫問題
#include <iostream>
class CTest1
{
public:
CTest1(int n) :m_Test1(n)
{
std::cout << "我是Test1成員構造" << std::endl;
}
~CTest1()
{
std::cout << "我是Test1成員析構" << std::endl;
}
public:
int m_Test1;
};
class CTest2
{
public:
CTest2(int m) :m_Test2(m)
{
std::cout << "我是Test2成員構造" << std::endl;
}
~CTest2()
{
std::cout << "我是Test2成員析構" << std::endl;
}
public:
int m_Test2;
};
class CBase1
{
public:
CBase1(int n) :m_BaseA(n)
{
std::cout << "我是1父類構造" << std::endl;
}
~CBase1()
{
std::cout << "我是1父類析構" << std::endl;
}
int m_BaseA;
};
class CBase2
{
public:
CBase2(int m) :m_BaseA(m)
{
std::cout << "我是2父類構造" << std::endl;
}
~CBase2()
{
std::cout << "我是2父類析構" << std::endl;
}
int m_BaseA;
};
class CA :public CBase1,public CBase2
{
public:
CA() : obj2(20),m_A(2), obj1(10), CBase2(6), CBase1(5)
{
std::cout << "我是子類構造" << std::endl;
}
~CA()
{
std::cout << "我是子類析構" << std::endl;
}
CTest1 obj1;
CTest2 obj2;
int m_A;
};
int main()
{
CA obj;
return 0;
}
總結:
1.構造優先呼叫父類,再呼叫成員,最後呼叫自己
2.析構順序和構造順序相反
3.父類構造需要傳參,就在子類的初始化列表中主動呼叫父類的構造傳參
4.成員的構造需要傳參,也需要在類的初始化列表中主動呼叫成員的構造傳參
5.成員的構造順序和定義順序一致
6.父類的構造順序和繼承順序一致
1.6 多繼承的問題
#include <iostream>
class CBase1
{
public:
CBase1(int n) :m_Base1(n)
{
std::cout << "我是1父類構造" << std::endl;
}
~CBase1()
{
std::cout << "我是1父類析構" << std::endl;
}
int m_Base1;
};
class CBase2
{
public:
CBase2(int m) :m_Base2(m)
{
std::cout << "我是2父類構造" << std::endl;
}
~CBase2()
{
std::cout << "我是2父類析構" << std::endl;
}
int m_Base2;
};
class CA :public CBase1, public CBase2
{
public:
CA() : m_A(2), CBase2(6), CBase1(5)
{
std::cout << "我是子類構造" << std::endl;
}
~CA()
{
std::cout << "我是子類析構" << std::endl;
}
public:
int m_A;
};
int main()
{
//子類多繼承的時候,和單繼承差不多的,擁有所有父類的所有成員
CA obj;
obj.m_Base1 = 10; //從CBase1繼承來的
obj.m_Base2 = 20; //從CBase2繼承來的
obj.m_A = 30; //這個是它自己的
return 0;
}
具體示例:
#include <iostream>
class Cpeople
{
public:
void Run()
{
std::cout << "我在快樂的跑步" << std::endl;
}
public:
char m_szName[20];
int m_nGender;
int nAge;
};
class CSpider//蜘蛛
{
public:
void Tusi()
{
std::cout << "Piu" << std::endl;
}
public:
int nAge;
};
class CSuperHero
{
public:
void SaveWorld()
{
std::cout << "拯救世界" << std::endl;
}
int nAge;
};
class CSPiderMan :public Cpeople, public CSpider,public CSuperHero
{
public:
void BianShen()
{
std::cout << "Piu" << std::endl;
}
};
int main()
{
CSuperHero obj1;
obj1.SaveWorld();
CSPiderMan obj;
obj.Run();
obj.Tusi();
obj.BianShen();
obj.SaveWorld();
//注意多繼承的時候,多個父類如果有同名成員,會造成二義性問題。
//可以使用作用域防止BUG產生
//更好的方式是使用虛繼承
obj.CSpider::nAge = 5;
obj.Cpeople::nAge = 20;
obj.CSuperHero::nAge = 15;
return 0;
}
2. 名稱空間
名稱空間機制是為了防止命名衝突
math1.h
#pragma once
namespace Code1
{
int GetMax(int a, int b);
int GetAdd(int a, int b);
}
math1.cpp
namespace Code1
{
int GetMax(int a, int b)
{
if (a > b)
{
return a;
}
else
{
return b;
}
}
int GetAdd(int a, int b)
{
return a + b;
}
}
math2.h
#pragma once
namespace Code2
{
int GetMax(int a, int b);
int GetAvg(int a, int b);
namespace inner
{
int GetMin(int a, int b);
}
}
math2.cpp
namespace Code2
{
int GetMax(int a, int b)
{
if (a < b)
{
return b;
}
else
{
return a;
}
}
}
namespace Code2
{
int GetAvg(int a, int b)
{
return (a + b) / 2;
}
namespace inner
{
int GetMin(int a, int b)
{
if (a < b)
{
return a;
}
else
{
return b;
}
}
}
}
主檔案
#include "Math1.h"
#include "Math2.h"
using Code1::GetMax;
using Code2::GetAvg;
namespace mi = Code2::inner;
int main()
{
GetMax(10, 20);
GetAvg(10, 15);
mi::GetMin(5, 10);
return 0;
}
補充作業:
1.設計一個狼人類,繼承自人類和狼類。
a.人類:學習方法 有年齡屬性 有參構造進行初始化
b.狼類:攻擊方法 有奔跑速度屬性 有參構造進行初始化
c.狼人類:變身方法 有攻擊力屬性 有參構造進行初始化
定義正確定義狼人物件,能夠呼叫繼承過來的所有函式
#include<iostream>
using namespace std;
class people
{
public:
people(int age) :m_age(age) //建構函式使用
{
}
void study()
{
cout << "i am study now" << endl; //學習方法
}
private:
int m_age;
};
class wolf
{
public:
wolf(int speed) :m_speed(speed) //建構函式使用
{
}
void attack()
{
cout << "i am fighting here" << endl;
}
private :
int m_speed;
};
class wolfman :public people, public wolf //多繼承,繼承多個成員的屬性
{
public:
wolfman(int power,int age, int speed):power(power),people(age),wolf(speed)//建構函式,用於初始化成員
{
}
void bianshen()
{
cout << "update!" << endl;
}
private:
int power; //初始化新定義的power
};
int main()
{
wolfman obj(100, 20, 70);
obj.study();
obj.attack();
obj.bianshen();
return 0;
}