你们的思惟模式会从面向过程到面向对象。处理更复杂程序。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以及指针置空。