你們的思惟模式會從面向過程到面向對象。處理更復雜程序。ios
學完以後,搞一個迷宮的尋路程序。c++
人類忠實的朋友:程序員
狗有本身的狗狗信息,也有它的技能。一條狗就是一個對象,多條狗爲了便於管理,爲一大羣狗創建起表格。
除過表格信息,他們還有共同技能: 叫和跑
從上面的狗狗,咱們就能夠抽象出一個狗類。數組
數據成員(屬性)、成員函數(方法)
思考: 抽象出來的是不是狗的所有信息函數
結論:目的不一樣抽象出的信息不一樣,咱們只抽象出咱們本身須要的信息。
經過電視機上的銘牌,咱們能夠獲得它的屬性信息。經過旋鈕等,咱們能夠操做電視機。
封裝: 選擇性暴露(把實現細節封裝起來,只暴露給用戶他們關心的細節)。學習
這些信息都在類中進行了定義,如何把想暴露的暴露出去,把想隱藏的隱藏起來呢?this
訪問限定符: public公共的,protected受保護的, private私有的
對象的實例化編碼
從類中將對象實例化出來,就是計算機根據類這個模板,製造出多個對象的過程。
實例化對象的兩種方式:spa
從棧中實例化對象.net
2-2-StackInstantiatedObject/main.cpp
#include <iostream> #include <stdlib.h> using namespace std; class TV { public: char name[20]; int type; void changeVol(); void power(); }; int main(void) { TV tv;//定義一個對象 // TV tv[20]; //定義一個對象數組 return 0; }//從棧中實例化對象 會自動回收
注意,務必要在類定義完成以後加上
;
從堆中實例化對象.
int main(void) { TV *p = new TV(); //在堆中實例化一個對象 // TV *q = new TV[20]; // 定義一個對象數組 delete p; // delete []q; return 0; }//從堆中實例化對象
new運算符申請出來的內存就是在堆上的了。
實例化出的對象固然不是一個擺設,咱們要經過訪問對象的各類成員來達成目的。
經過不一樣方式實例化出來的對象,在對象成員,成員函數上的訪問方式也不一樣。
棧實例化出來的對象使用.
進行對象成員訪問。
int main(void) { TV tv;//定義一個對象 tv.type = 0; tv.changeVol(); return 0; }//從棧中實例化對象 自動回收
堆實例化出來的對象使用->
進行對象成員訪問。
int main(void) { TV *p = new TV(); p -> type = 0; p -> changeVol(); delete p; p = NULL; return 0; }//從堆中實例化對象
當堆中實例化的對象是數組時,代碼示例以下:
int main(void) { TV *q = new TV[5]; for (int i = 0; i < 5; ++i) { p[i] ->type =0; p[i] ->changeVol(); } delete []q; p = NULL; return 0; }//從堆中實例化對象
定義一個座標類:包含x,y兩個數據成員。分別打印x和打印y成員函數。
類名最好要能看出類的功能
2-2-CoordinateClassStackHeap/main.cpp
#include <stdlib.h> #include <iostream> using namespace std; class Coordinate { public: int x; int y; void printx(){ cout << x << endl; } void printy() { cout << y << endl; } }; int main(void) { Coordinate coor; coor.x = 10; coor.y = 20; coor.printx(); coor.printy(); Coordinate *p = new Coordinate(); if (NULL == p) { //failed return 0; } p->x = 100; p->y = 200; p->printx(); p->printy(); delete p; p = NULL; system("pause"); return 0; }
申請內存失敗狀況處理,釋放內存,指針置空。
使用頻繁,操做繁瑣的數據:
這些都是用的頻繁但操做簡單的數據類型。
字符串數組操做函數: (strlen | strcat | strcpy | strcmp | strncpy |strncmp | strstr)
3-1-stringDemo/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; int main(void) { string name = "mtianyan"; string hobby("football"); cout << name << endl; cout<<hobby << endl; system("pause"); return 0; }
注意引入string.h,其命名空間也是std
初始化string對象的多個方式
string s1; //s1爲空串 string s2("ABC"); //用字符串字面值初始化s2; string s3(s2); //將s3初始化爲s2的一個副本。 string s4(n,'c') //將s4初始化爲字符‘c’的n個副本。3個ccc s4='ccc'
string的經常使用操做
s.empty() //若s爲空串,返回true,不然返回false; s.size() //返回s中字符的個數 s[n] //返回s中位置爲n的字符,位置從0開始 s1 + s2 //將兩個字符串鏈接成新串,返回新生成的串 s1 = s2 //把s1的內容替換爲s2的副本; v1 == v2 //斷定相等,相等返回true,不然返回false v1 != v2 //斷定不等,不等返回true,不然返回false
經過點的方式,說明s是一個對象
s1+s2 的思惟陷阱
純字符串鏈接爲非法操做。只有純字符串和string,以及string與string是合法的。
由於要斷定輸入是否是爲空。因此不能簡單的使用cin而應該使用getline
3-1-NameStringDemo/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; int main(void) { string name; cout << "please input your name:"; getline(cin, name); if (name.empty()){ cout << "input is null" << endl; system("pause"); return 0; } if (name == "mtianyan") { cout << "you are a admin" << endl; } cout << "hello ," + name << endl; cout << "your name length is :" << name.size() << endl; cout << "your name frist letter is :" << name[0] << endl; system("pause"); return 0; }
c++中經過
getline
獲取外界控制檯輸入(當包含可能輸入爲空狀況)。通常狀況仍是cin好了。
管理員:
名字爲空:
其餘普通名字
定義一個Student類,包含名字和年齡兩個數據成員,實例化一個Student對象,並打印出其成兩個數據成員
3-2-StudentClassString/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; /** * 定義類:Student * 數據成員:名字、年齡 */ class Student { public: // 定義數據成員名字 m_strName 和年齡 m_iAge string m_strName; int m_iAge; }; int main() { // 實例化一個Student對象stu Student stu; // 設置對象的數據成員 stu.m_strName = "mtianyan"; stu.m_iAge = 21; // 經過cout打印stu對象的數據成員 cout << stu.m_strName << " " << stu.m_iAge << endl; system("pause"); return 0; }
以前咱們的用法是如上圖所示。可是這樣是不符合面向對象的指導思想。
面向對象的基本思想:(以對象爲中心,以誰作什麼表達程序邏輯)
封裝的好處: 符合面向對象的思想,在set中對於參數條件進行限制(防止數據非法,如年齡1000)
數據只讀不寫(只讀屬性):只寫get方法,不寫set方法。
輪子個數應該只能讀,而不能被外界改變設置。
定義一個student類,含有以下信息
4-2-StudentEncapsulation/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; class Student { public: string getName() const { return m_strName; } void setName(string _name) { m_strName = _name; } string getGender() { return m_strGender; } void setGender(string val) { m_strGender = val; } int getScore() { return m_iScore; } void study(int _score) { m_iScore += _score; } void initScore() { m_iScore = 0; } private: string m_strName; string m_strGender; int m_iScore; }; int main(void) { Student stu; stu.initScore(); stu.setName("天涯明月笙"); stu.setGender("男"); stu.study(5); stu.study(3); cout << stu.getName() << " " << stu.getGender() << " " << stu.getScore() << endl; system("pause"); return 0; }
不賦初值:
注意,賦初值。後面會學到構造函數,它是專用來初始化的。
定義一個Student類,包含名字一個數據成員,使用get和set函數封裝名字這個數據成員。在main函數中經過new實例化對象,並打印其相關函數。
4-3-StudentHeapInstance/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; /** * 定義類:Student * 數據成員:m_strName * 數據成員的封裝函數:setName()、getName() */ class Student { public: // 定義數據成員封裝函數setName() void setName(string _name) { m_strName = _name; } // 定義數據成員封裝函數getName() string getName() const { return m_strName; } //定義Student類私有數據成員m_strName private: string m_strName; }; int main() { // 使用new關鍵字,實例化對象 Student *str = new Student(); // 設置對象的數據成員 str->setName("mtianyan"); // 使用cout打印對象str的數據成員 cout << str ->getName() << endl; // 將對象str的內存釋放,並將其置空 delete str; str = NULL; system("pause"); return 0; }
內聯函數關鍵字: inline
inline void fun() { cout << "hello" << endl; }
類內定義與類外定義
不會把inline寫出來,可是會優先編譯爲inline
類外定義分爲兩種:
同文件類外定義例子: Car.cpp
爲了標識這是屬於汽車car的成員函數:car::
同文件類外定義是突擊隊的話,分文件類外定義就是正規軍了。
幾乎全部的c++程序,專業點的程序員都會分文件類外定義。
分文件類外定義
一個.h頭文件,名稱與類名一致。必須包含.h文件,並使用
car::
要求:
5-2-1-OutClassDefine1/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; class Teacher { public: void setName(string name); string getName(); void setGender(std::string val); string getGender(); void setAge(int _age); int getAge(); void teach(); private: string m_strName; string m_strGender; int m_iAge; }; string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } string Teacher::getGender() { return m_strGender; } void Teacher::setGender(string val) { m_strGender = val; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; } void Teacher::teach() { cout << "如今上課" << endl; }; int main(void) { Teacher teacher; teacher.setAge(21); teacher.setName("mtianyan"); teacher.setGender("男"); cout << teacher.getName() << " " << teacher.getGender() << " " << teacher.getAge() << endl; teacher.teach(); system("pause"); return 0; }
5-2-2-MultipleFilesOutClassDefine/main.cpp
在解決方案,頭文件處右鍵添加新建項:頭文件: Teacher.h
在源文件右鍵添加新建項:cpp: Teacher.cpp
Teacher.h只存放類的聲明
#include <string> using namespace std; class Teacher { public: void setName(string name); string getName(); void setGender(std::string val); string getGender(); void setAge(int _age); int getAge(); void teach(); private: string m_strName; string m_strGender; int m_iAge; };
Teacher.cpp:只存放類外方法的定義
#include "Teacher.h" #include <iostream> #include <string> using namespace std; string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } string Teacher::getGender() { return m_strGender; } void Teacher::setGender(string val) { m_strGender = val; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; } void Teacher::teach() { cout << "如今上課" << endl; };
main.cpp存放類的實例化等以及程序主入口
#include <stdlib.h> #include <iostream> #include <string> #include "Teacher.h" using namespace std; int main(void) { Teacher teacher; teacher.setAge(21); teacher.setName("mtianyanMultiple"); teacher.setGender("男"); cout << teacher.getName() << " " << teacher.getGender() << " " << teacher.getAge() << endl; teacher.teach(); system("pause"); return 0; }
main.cpp的頭文件爲:
#include <stdlib.h> #include <iostream> #include "Teacher.h" using namespace std;
Teacher.h的頭爲
#include <string> using namespace std;
tercher.cpp的頭爲:
#include "Teacher.h" #include <iostream> #include <string> using namespace std;
注意:不要把<string>
寫成<string.h>
內存分區
int x=0;
int*p=NULL;
內存由系統管理。int *p = new int[20];
new & deletestring str = "hello";
定義一個汽車類,在類被實例化以前,是不會佔用棧或者堆內存的。
可是當它實例化出car1 ,car2 ,car3 。每一個對象在棧上開闢一段內存,用來存儲各自的數據。不一樣的變量,佔據不一樣的內存。
代碼區只有一份代碼。
坦克大戰,遊戲開始初始化坦克位置。
對象初始化分爲兩種:
對於有且僅有一次的初始化:
如,咱們寫代碼忘記調用了初始化函數,重複調用了初始化函數。
構造函數的規則和特色:
構造函數在對象實例化時別調用,且僅被調用一次
class Student { public: Student(){ m_strName = "jim"; } // 與類名相同,無返回值。 private: string m_strName; }
class Student { public: Student(string name){ m_strName = name; } private: string m_strName; }
class Student { public: Student(){ m_strName = "jim"; } Student(string name){ m_strName = name; }// 重載: 參數個數不一樣,參數類型不一樣,參數調用順序不一樣。 private: string m_strName; }
Teacher(); Teacher(string name, int age=20);
這樣的計算機是能夠分辨的,可是若是給name也給默認值。那麼將沒法經過編譯。
提示重載函數的調用不明確。兩個不能共存,可是能夠單獨存在。
6-2-ConstructorFunction
Teacher.h
#include <string> using namespace std; class Teacher { public: Teacher(); Teacher(string name, int age=20); void setName(string name); string getName(); void setAge(int _age); int getAge(); void teach(); private: string m_strName; int m_iAge; };
teacher.cpp
#include "Teacher.h" #include <iostream> #include <string> using namespace std; Teacher::Teacher() { m_strName = "jim"; m_iAge = 5; cout << "Teacher()" << endl; } Teacher::Teacher(string name, int age) { m_strName = name; m_iAge = age; cout << "Teacher(string name, int age)" << endl; } string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; } void Teacher::teach() { cout << "如今上課" << endl; };
main.cpp
#include <iostream> #include <string> #include "Teacher.h" using namespace std; int main(void) { Teacher teacher; //無參數實例化,這裏能正常運行是由於咱們沒有給參數都加默認值。 Teacher t2("merry", 15);//有參數實例化 Teacher t3("james");//,構造函數有默認值20 cout << teacher.getName() << " " << teacher.getAge() << endl; cout << t2.getName() << " " << t2.getAge() << endl; cout << t3.getName() << " " << t3.getAge() << endl; teacher.teach(); system("pause"); return 0; }
構造函數除了重載還能夠給參數賦默認值。不調用時,編譯能夠經過的。
不管從堆中仍是棧中實例化對象,都有一個特色是不用傳參數。
這樣的構造函數能夠像如上圖所示定義,自己不帶參數。
或帶了參數的同時攜帶着全部參數的默認值。
Student(){} Student(string name = "jim");
class Student { public: Student():m_strName("jim"),m_iAge(10){} //構造函數初始化列表進行初始化 private: string m_strName; int m_iAge; }
構造函數後面使用一個冒號隔開,對於多個數據成員初始化,中間要由逗號隔開。賦值要用括號。
思考: 表面看起來初始化列表的工做是能夠由構造函數代勞的,要它還有何用?
下面舉例說明。
計算圓,其中pi是一個常量,因此用const修飾。
class Circle { public: Circle(){m_dPi=3.14} //錯誤,給常量賦值 private: const double m_dPi; }
沒法對於咱們的靜態成員變量在構造函數中賦初值,解決方案: 在初始化列表中賦初值。
class Circle { public: Circle():m_dPi(3.14){} // 正確,使用初始化列表 private: const double m_dPi; }
定義有參默認構造函數,使用初始化列表初始化數據。
6-5-ParameterConstructorFunctionInitList
Teacher.h
#include <string> using namespace std; class Teacher { public: Teacher(string name ="hi", int age=1,int m =100); // 有參默認構造函數 void setName(string name); string getName(); void setAge(int _age); int getAge(); int getMax(); void setMax(int m_iMax); private: string m_strName; int m_iAge; const int m_iMax; };
Teacher.cpp
#include "Teacher.h" #include <iostream> #include <string> using namespace std; Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age),m_iMax(m) { cout << "Teacher(string name, int age)" << endl; } string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; } int Teacher::getMax() { return m_iMax; } void Teacher::setMax(int m_iMax) { m_iMax = m_iMax; }
main.cpp
#include <stdlib.h> #include <iostream> #include <string> #include "Teacher.h" using namespace std; int main(void) { Teacher teacher("merry",12,150); cout << teacher.getName() << " " << teacher.getAge() <<" "<<teacher.getMax()<< endl; system("pause"); return 0; }
一個類能夠沒有默認構造函數, 有別的構造函數也能夠實例化對象。在構造函數聲明的時候加了默認值,就不須要在定義的時候加默認值。
常量若是不在初始化列表中初始化,會提示必須在初始值設定項列表中初始化
上圖,這是咱們定義的Student類
使用時。
int main() { Student stu1; Student stu2 =stu1; Student stu3(stu1); return 0; }
實例化了三個對象,但上述代碼只會執行一次構造函數內的代碼。
三次實例化調用了構造函數,但不是咱們本身定義的,而是拷貝構造函數。
實例化對象必定會調用構造函數,可是像上面代碼這種,將不使用咱們定義的構造函數,而使用拷貝構造函數。
拷貝構造函數:
定義格式: 類名(const 類名& 變量名)
Student(){ m_strName = "jim"; } Student(const Student& stu){ }
傳入一個引用。
stu3(stu1);
)或者複製初始化(stu2 =stu1;
)實例化對象時系統自動調用拷貝構造函數構造函數總結:
有參構造函數:
系統自動生成的函數:
一旦咱們自行定義,系統就不會再生成了。
初始化列表:
6-8-CopyConstructorFunction
Teacher.h
#include <string> using namespace std; class Teacher { public: Teacher(string name ="mtianyan", int age=21,int m =100); Teacher(const Teacher& tea); //拷貝構造函數 void setName(string name); string getName(); void setAge(int _age); int getAge(); private: string m_strName; int m_iAge; };
teacher.cpp
#include "Teacher.h" #include <iostream> #include <string> using namespace std; Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age) { cout << "Teacher(string name, int age)" << endl; } Teacher::Teacher(const Teacher& tea) { cout << "Teacher(const Teacher &tea)" << endl; } string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; }
main.cpp
#include <stdlib.h> #include <iostream> #include <string> #include "Teacher.h" using namespace std; void test(Teacher t) { } int main(void) { Teacher teacher; Teacher t2 = teacher; Teacher t3(t2); // 這裏不管使用t2仍是teacher都只會調用拷貝構造函數 test(teacher); //函數使用已實例化的對象時調用。 cout << teacher.getName() << " " << teacher.getAge() << endl; system("pause"); return 0; }
函數使用已實例化的對象做爲參數時就會調用拷貝構造函數。如test()
拷貝構造函數的參數是肯定的,不能重載
構造函數是對象來到世界的第一聲哭泣
析構函數是對象離開世界的最後一聲嘆息。
析構函數在對象銷燬時會自動調用,歸還系統資源:
定義格式:
~類名() //不加任何的參數
class Student { public: Student(){cout << "Student" << endl;} ~Student(){cout << "~Student" << endl;} }
析構函數不容許加任何參數。
析構函數的價值:
若是咱們在定義數據時使用了指針,使用指針指向了堆中分配的內存(new)。在對象銷燬時咱們要釋放這些內存,釋放這些內存最好時機是對象被銷燬以前。
class Student { public: Student(){m_pName = new char[20];} ~Student(){delete []m_pName;} private: char *m_pName; }
對象的生命歷程:
按回車後一瞬間能夠看到析構函數被調用
6-11-DestructorFunction
Teacher.h
#include <string> using namespace std; class Teacher { public: Teacher(string name ="mtianyan", int age=21,int m =100); // 構造 Teacher(const Teacher &tea); // 拷貝構造 ~Teacher(); // 析構 void setName(string name); string getName(); void setAge(int _age); int getAge(); private: string m_strName; int m_iAge; };
Teacher.cpp
#include "Teacher.h" #include <iostream> #include <string> using namespace std; Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age) { cout << "Teacher(string name, int age)" << endl; } Teacher::Teacher(const Teacher &tea) { cout << "Teacher(const Teacher &tea)" << endl; } Teacher::~Teacher() { cout << "~Teacher()" << endl; } string Teacher::getName() { return m_strName; } void Teacher::setName(string name) { m_strName = name; } int Teacher::getAge() { return m_iAge; } void Teacher::setAge(int _age) { m_iAge = _age; }
main.cpp
#include <stdlib.h> #include <iostream> #include <string> #include "Teacher.h" using namespace std; void test(Teacher t) { } int main(void) { Teacher t1; Teacher t2(t1); Teacher *p = new Teacher(); delete p; p = NULL; system("pause"); return 0; }
在按下回車的瞬間,能夠看到兩行輸出以下。這是咱們t1 t2在銷燬時調用析構函數。
~Teacher()
堆棧中的對象在銷燬時都會自動調用析構函數。
梳理前面學過的 & 劇透後面的
圍繞類與對象展開。
類由成員函數和數據成員組成。擔憂本身的類與其餘人重名,類之上能夠定義命名空間。
數據成員:
普通數據成員,(數據類型普通) int, char, char[], string;
初始化列表(const成員);靜態數據成員;對象成員
成員函數:
對於數據成員進行封裝, 屬性封裝函數(get,set); 通常功能函數;特殊函數:構造函數(根據參數不一樣,拷貝構造函數-默認構造函數);析構函數.
成員函數 (參數默認值;函數重載;引用;const;)
對象實例化(堆中實例化,棧中實例化)
對象能夠是個引用?對象能夠用const修飾麼?
定義一個Student類,包含名字一個數據成員,定義無參構造函數、有參構造函數、拷貝構造函數、析構函數及對於名字的封裝函數,在main函數中實例化Student對象,並訪問相關函數,觀察運行結果。
7-2-StudentDemo/main.cpp
#include <stdlib.h> #include <iostream> #include <string> using namespace std; /** * 定義類:Student * 數據成員:m_strName * 無參構造函數:Student() * 有參構造函數:Student(string _name) * 拷貝構造函數:Student(const Student& stu) * 析構函數:~Student() * 數據成員函數:setName(string _name)、getName() */ class Student { public: Student() { m_strName = ""; }; Student(string _name) { m_strName = _name; }; Student(const Student& stu) { }; ~Student() { }; void setName(string _name) { m_strName = _name; }; string getName() { return m_strName; }; private: string m_strName; }; int main(void) { // 經過new方式實例化對象*stu Student *stu = new Student(); // 更改對象的數據成員爲「mtianyan」 stu->setName("mtianyan"); // 打印對象的數據成員 cout << stu->getName() << endl; delete stu; stu = NULL; system("pause"); return 0; }
注意new方式實例化的對象不要忘記delete以及指針置空。