凡科建设网站还用买服务器吗,跑步机 东莞网站建设,买CAD设计图做的网站,网站前后台代码特性关系
C语言面向过程C面向过程 面向对象(封装 继承 多态)C具备C语言的全部特性的基础上#xff0c;并且支持更多新的特性
内存泄露
申请内存#xff0c;没有释放申请 malloc new释放 free deleteProcessExplorer查看内存是否释放
代码移植
将生成的exe运行在别的平台面向过程 面向对象(封装 继承 多态)C具备C语言的全部特性的基础上并且支持更多新的特性
内存泄露
申请内存没有释放申请 malloc new释放 free deleteProcessExplorer查看内存是否释放
代码移植
将生成的exe运行在别的平台不因为缺乏环境而出错使用软件 ProcessExplorer软件查找exe程序执行需要的dll等配置文件或者在程序编译的时候整合对应的库文件(属性-C/C - 代码生成 -运行库)多线程 MT 静态编译 - 链接库文件多线程调试 MTH 静态编译 - 链接库文件多线程 DLL MD多线程调试DLL MDT
pragma once
确保.h文件减少包含减少编译错误
C输入输出方式
输入 输入设备- 输入流-scanf/cin - 变量 cinab;输出 变量 - printf/cout - 输出流 - 输出设备 cout a endl;endl输出换行不需要关注 数据类型 和 占位符号
命名空间
std 关键字 namespace 不同的命名空间使用相同的变量名字和函数使用 命名空间:: 函数名 进行函数的调用数据类型
布尔类型 bool 真 true假 falsebool flag false;std::cout boolalpha flag endl;//输出false特性C随用随定义
引用
一般作为函数的参数这样就不需要返回数据直接取元素的数值 引用避免了申请多个变量大家对引用的使用都是针对同一个元素进行函数默认值
缺省值 default value函数使用默认值 void fun(int i,int j 7,int k 8) 有默认值的参数必须在参数表的最右端声明和定义同时给出默认值有些编译器会报错。最好在函数的声明时给出默认值
void fun(int i,int j 7,int k 8){std::cout i i std::endl;std::cout j j std::endl;std::cout k k std::endl;
}int main(void)
{int i 7;fun(i);return 0;
}
函数的重载
重载函数的参数的个数、参数的类型、参数的顺序三者中必须至少有一种不同返回值不同不作为重载依据重载函数的功能应该相近不可以为了重载而重载main函数不可以重载
内存管理
栈区由编译器自动分配释放 char型指针p全局区静态区static存放全局变量、静态数据、常量 int x 0;x属于全局变量文字常量区常量字符串就是存放在这里程序结束之后由系统释放 文字常量 Hello程序代码区存放函数体的二进制代码 整个代码的二进制数据堆区程序员申请/释放内存 由程序员申请释放
int x 0;
int main(void)
{char *p Hello;x 10;return 0;
}
申请内存 new 释放内存 deleteint *p new int; delete p;int *p new int(10); delete p;int *p new int[5]; delete []p;内存申请有可能失败 if (p null) return False; p null;最后让指针指向null内存忘记释放会产生内存的泄露如果需要大的内存 启用大地址输入和输出都是相对于内存来说的内存的特点是 有电则生断电则往文件的输出 常量const
关键字 修饰一个变量使其成为常量const int x 0; 等价于 int const x 0const int * p 0; 等价于 int const * p 0;int * const p 0;const int * const p 0; 等价于 int const * const p 0;const 修饰普通数据类型 const int x 0; x10;错误const修饰指针类型 int const *p x; const修饰*因此p可以变化但是*p 不可以int * const p x;const 修饰p因此*p可以变化 但是p不可以//const 修饰指针类型int x0,y0;int const *p x;*p 4;//错误p y;//正确 //const 修饰指针类型int x0,y0;int *const p x;*p 4;//错误p y;//正确
const修饰函数参数int max(const int x,const int y) 代码内部无法对其修改int max(int const *p,int *const q)#define 和const定义常量的区别#define是预编译器只进行字符串替换const在编译的时候进行参数的检查
类和对象之间的关系
类 抽象对象 具体class类关键字 类名 数据成员 成员函数访问限定符号 public private protected 访问限定符号 可以多次使用
class Student{
public:char name[20];int gender;int age;void study(){std::cout learning! std::endl;}private:};
属性的封装
通过set和get的方式对成员变量进行封装通过函数将private私有属性 进行返回
类的实例化
栈中定义 Car car; 操作系统会回收资源堆中定义 Car *p new Car(); 资源需要由程序员进行释放
类成员的访问
栈中定义 Car car; car.getWheelCount(); . 操作系统释放资源堆中定义 Car *p new Car(); p-getWheelCount(); - delete p; p nullptr;相似于结构体(struct)类的默认访问权限是private 结构体的默认访问权限是publicstruct结构体的使用和函数类似也可以使用上述 堆和栈的方式进行定义Student *p new Student[20];//定义20个学生变量p[0].age 10;控制第一个学生的age变量 使用delete[] p删除内存空间
#include iostream
#include fstream
#include sstreamstruct Student{
public:char name[20];int age;void study(){std::cout learning! std::endl;}private:int gender;int money;
};
int main(void)
{Student student{};student.study();Student *p new Student[20];p[0].study();delete []p;p nullptr;return 0;
}
类内定义和类外定义
类内定义 建议编译器使用内联方式进行编译如图所示普通函数需要进行函数的跳转而内联函数将需要跳转的函数拷贝到指定的位置避免了函数的拷贝即避免了2和4的步骤只剩下3的步骤并且使用实参进行参数的替代逻辑简单的函数才会使用内联的方式如果函数很复杂就按照普通函数处理内联函数和宏函数
宏函数走预编译器 内联函数走编译器宏函数没有参数类型的要求不会进行参数检查
类内定义和类外定义 命名规范
类名单词首字母大写文件名字 和 类名 相同函数名第二个单词首字母大写由单词或动宾短语构成数据成员以(m_类型)作为前缀 int m_i m作为类内成员变量
对象的存储结构
类申明多个对象每个对象占据不同的地址类内的函数 只有一份共同调用
构造函数
实例化对象的时候 自动调用构造函数名字 和 类名相同并且没有返回值如果没有自定义构造函数系统提供默认构造函数构造函数可以使用 带参数的进行初始化构造函数 可以重载系统构造函数 Student(){} 也叫默认构造函数有了自定义的构造函数系统的构造函数还可以使用吗不可以。如果自定义了带参数构造函数使用的时候使用不带参数的默认构造函数会造成错误。因为用户定义了构造函数系统就不会再产生默认构造函数
初始化列表 使用Car():m_iWheelCount(4);进行初始化初始化列表 伴随着构造函数在构造函数的后面使用 : 进行对变量的赋值即使是const修饰的变量 也可以使用初始化列表进行 赋值
#include iostream
#include fstream
#include sstreamclass Car{
public:Car():m_iWheelCount(4),m_iSpeed(0){};void getWheelCount(){std::cout m_iWheelCount std::endl;std::cout m_iSpeed std::endl;}
private:const int m_iWheelCount;int m_iSpeed;
};
int main(void)
{Car car;car.getWheelCount();return 0;
}
拷贝构造函数
对象初始化的两种方式 直接初始化 int x 1024;复制初始化 Student stu(stu1);如上图所示 stu1 使用默认构造函数stu2 和 stu3 使用 拷贝构造函数拷贝构造函数的特点1如果没有自定义的拷贝构造函数 则系统自动生成2当对象直接初始化 或者 复制初始化 时候 自动调用拷贝构造函数3当自定义了拷贝构造函数则系统 不再生成 默认的拷贝构造函数
#include iostream
#include fstream
#include sstreamclass Car{
public:Car(){};Car(const Car car){ //拷贝构造初始化std::cout Car(const Car car) std::endl;}
private:};
int main(void)
{Car car1; //直接初始化Car car2(car1); //拷贝构造初始化Car car3 car1; //拷贝构造初始化return 0;
}
类的析构函数
析构函数名字 如 ~类名(){}析构函数没有参数析构函数没有返回数值析构函数不可以重载析构函数 销毁时候 自动执行堆 和 栈 使用一致
#include iostream
#include fstream
#include sstreamclass Car{
public:Car(){std::cout 对象创建 std::endl;};~Car(){std::cout 对象销毁 std::endl;}
private:};
int main(void)
{Car *car new Car();delete car;car nullptr;return 0;
}
什么时候需要自定义析构函数比如在堆上申请了一大段内存空间使用delete []p进行资源的释放
#include iostream
#include fstream
#include sstreamclass Car{
public:Car(){m_pName new char [20];std::cout 对象创建 std::endl;};~Car(){delete[] m_pName;std::cout 对象销毁 std::endl;}
private:char *m_pName;
};
int main(void)
{Car *car new Car();delete car;car nullptr;return 0;
}
当函数参数是一个对象的时候使用拷贝构造函数
对象成员
多个类之间互相使用调用使用得到对象
对象数组
#include iostreamclass Coordinate{
public:int m_iX;int m_iY;
};
int main(void)
{Coordinate coord[3]; //栈上申请内存coord[1].m_iX 4;Coordinate *p new Coordinate[3]; //堆上申请内存p[0].m_iX 20;delete []p;//删除对象数组p nullptr;return 0;
}
应该使用delete []p方式删除对象数组不加上[] 只会清除一个对象
静态数据成员
静态数据成员 放在全局区静态数据成员 没有对象仍然可以使用静态数据成员 与 类 同生共死静态成员函数 只可以使用 静态数据成员 非静态成员函数 可以使用 静态数据成员
#include iostreamclass Tank{
public:Tank(std::string code);~Tank();void attack();static int getCount();
private:std::string m_strCode;static int g_iCount;
};
int Tank::g_iCount 0;Tank::Tank(std::string code) {m_strCode code;g_iCount;
}Tank::~Tank() {g_iCount--;
}
void Tank::attack() {if (g_iCount 3){std::cout std::endl;} else{std::cout std::endl;}
}int Tank::getCount() {return g_iCount;
}int main(void)
{std::cout Tank::getCount std::endl;return 0;
}
对象指针成员
对象成员 如图左边所示对象A的构造函数需要先等对象B构造完成之后才执行对象A的构造函数对象成员指针 如图右边所示对象B的指针不会提前执行对象B的构造函数直接进行对象A的构造函数对象B的构造函数 伴随其过程而产生this指针
this进行同名变量之间使用this标记当前对象this指向这个类每个对象都有自己的this指针每个this指针都指向该对象的首地址#include iostreamclass Student{
public:Student(std::string name);Student* getIt1(); //获取当前对象的指针Student getIt2(); //获取当前对象void getIt3(Student **it); //获取当前对象指针void getIt4(Student it); //通过传入引用 获取当前对象void printName();//打印名字void setName(std::string name);//设置名字
private:std::string m_strName;
};Student::Student(std::string name) {this-m_strName name;
}Student * Student::getIt1() {return this;
}Student Student::getIt2() {return *this;//返回this指针指向的对象
}void Student::getIt3(Student **it) {*it this;
}void Student::getIt4(Student it) {it *this;
}void Student::printName() {std::cout m_strName std::endl;
}void Student::setName(std::string name) {m_strName name;
}
int main() {//测试 getIt1
// Student stu(zhangsan);
// Student *p stu.getIt1();
// p-printName();
// Student stu1(lisi);
// p stu1.getIt1();
// p-printName();//测试 getIt2
// Student stu(zhangsan);
// stu.printName();
// Student stu1 stu.getIt2();
// stu1.setName(lisi);
// stu1.printName();//测试 getIt3 *无法拿到指针指向的数据 需要使用**
// Student stu(zhangsan);
// Student *p nullptr;
// stu.printName();
// stu.getIt3(p); //注意
// p-printName();//测试 getIt4Student stu(zhangsan);Student stu1(liis);stu.getIt4(stu1);stu1.printName();return 0;
}常成员函数
在函数的名字后面添加 const表示这个函数是常成员函数在常成员函数中不能改变数据成员的数值const 也是函数重载的一个特性只有常对象 才可以使用 常成员函数常对象指针常对象引用
#include iostreamclass Student{
public:Student(std::string name);void printInfo();//常成员函数//重载函数void printInfo() const;
private:std::string m_strName;
};void Student::printInfo() {std::cout m_strName std::endl;
}void Student::printInfo() const {//在常成员函数中不能改变数据成员的数值std::cout m_strName std::endl;
}int main() {Student s1(zhangsan);s1.printInfo();const Student s2(lisi);//1常对象//修饰的时候 const 可以放在Student的前面 或者 后面s2.printInfo();Student *s3 new Student(Merry);s3-printInfo();//2常对象指针Student const *s4 new Student(Jim);s4-printInfo();Student s5(Merry);Student s6 s5;Student const s7 s5;//常对象引用s6.printInfo();s7.printInfo();return 0;
}深拷贝和浅拷贝
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体而不是引用。假设B复制了A修改A的时候看B是否发生变化如果B跟着也变了说明是浅拷贝修改堆内存中的同一个值如果B没有改变说明是深拷贝修改堆内存中的不同的值深拷贝和浅拷贝的区别
友元函数
friend 友元全局函数friend 类型 函数名字 (形参列表)友元成员函数 friend 类型 类名::函数名字(形参表)友元 不可以传递友元 是单向的私有private变量 通过申明为友元函数访问 私有变量friend友元函数 放在private public 都可以不影响使用但是一般放在类的最前边
友元全局函数 Time.h
#pragma onceclass Time{friend void printTime(Time t);// 友元全局函数(当前类 引用)
// friend void Student::setTime(Time t);//友元成员函数(类名 函数名字)
public:Time(int year,int month,int day){m_iYear year;m_iMonth month;m_iDay day;};
private:int m_iYear;int m_iMonth;int m_iDay;
};主函数
#include iostream
#include lib/Time.hvoid printTime(Time t);int main() {Time t1(2015,8,7);printTime(t1);return 0;
}void printTime(Time t){std::cout t.m_iYear - t.m_iMonth - t.m_iDay std::endl;
}友元成员函数
#includeiostream
using namespace std;class Date;//对Date类的提前引用声明
class Time
{
public:Time(int, int, int);//声明构造函数void display(Date );
private:int hour;int sec;int minute;
};class Date
{
public:Date(int, int, int);//声明构造函数friend void Time::display(Date );
private:int mouth;int day;int year;
};Time::Time(int h, int m, int s)
{hour h;minute m;sec s;
}void Time::display(Date d)//display的作用是输出年月日时分秒
{cout d.mouth / d.day / d.year endl;cout hour : minute : sec endl;
}Date::Date(int m, int d, int y)
{mouth m;day d;year y;
}int main(void)
{Time t1(10, 13, 56);Date d1(4, 15, 2019);t1.display(d1);return 0;
}友元 一般是在先前代码上进行使用准确说是一种设计缺陷的弥补措施尽量不要使用
继承
父类 叫做 基类子类 叫做 派生类父类是抽象子类是在父类抽象的基础上进一步的细化比如父类是人子类是工人子类的子类是 电工逐步细化
三种派生方式
公有派生 公有继承 public保护派生 保护继承 protected私有派生 私有继承 private多继承
一个类继承了多个类。比如童工类 继承 儿童类 的同时 也继承了 工人类
多重继承
类和类之间逐级继承。比如 技术工人类 继承 工人类电焊工人类 继承 技术工人类
菱形继承
类和类之间的继承关系构成了一个菱形比如 工人类 和 农民类都继承自 人类而农民工类 继承了工人类 和 农民类。四者之间构成了一个菱形为了解决这个问题需要使用虚继承 或者在 头文件引用 #pragma once
公有继承
如果未定义 默认使用 私有派生 私有继承 private子类 派生自 父类。所以先执行 父类的构造函数 再 执行 子类的构造函数销毁的时候先执行 子类的析构函数 再执行 父类的析构函数通过在父类 添加字段和方法影响子类。而且派生自不同的父类子类之间的影响是最低的父类 的 private成员子类无法访问
#include iostreamclass Worker{
public:Worker(std::string name,std::string code);~Worker();std::string getName() const;
protected:std::string m_strName;std::string m_strCode;
};Worker::Worker(std::string name,std::string code) {m_strName name;m_strCode code;
}Worker::~Worker() {}std::string Worker::getName() const {return m_strName;
}class Electrician : public Worker{
public:Electrician(std::string name,std::string code,size_t salary):Worker(name,code){m_iSalary salary;};void printInfo() const{std::cout m_strName m_strCode m_iSalary std::endl;}protected:size_t m_iSalary;
};int main(void)
{Electrician *electrician new Electrician(Jim,1234,1200000);std::cout electrician-getName() std::endl;electrician-printInfo();return 0;
}
三种继承方式
public 公有继承方式 父类的public 派生到 子类的public父类的protected 派生到 子类的protected父类的private 对于子类不可见protected 保护继承方式 父类的public 派生到 子类的protected父类的protected 派生到 子类的protected父类的private 对于子类不可见private 私有继承方式 父类的public 派生到 子类的private父类的protected 派生到 子类的privat父类的private 对于子类不可见
同名隐藏
父类 和 子类 的成员函数的名字一致参数无限制成员函数的名字一样但是所处的作用域不一样比如处于protected public等同名的时候 子类的函数名字 会 隐藏父类的名字但是父类的名字也是可以使用的。比如 使用子类定义的对象.父类的名字::同名的函数的名字();
#include iostreamclass Worker{
public:Worker(std::string name,std::string code);~Worker();std::string getName() const;void printInfo(){std::cout 父类 std::endl;};
protected:std::string m_strName;std::string m_strCode;
};Worker::Worker(std::string name,std::string code) {m_strName name;m_strCode code;
}Worker::~Worker() {}std::string Worker::getName() const {return m_strName;
}class Electrician : public Worker{
public:Electrician(std::string name,std::string code,size_t salary):Worker(name,code){m_iSalary salary;};void printInfo() const{std::cout m_strName m_strCode m_iSalary std::endl;std::cout 子类 std::endl;}protected:size_t m_iSalary;
};int main(void)
{Electrician *electrician new Electrician(Jim,1234,1200000);std::cout electrician-getName() std::endl;electrician-printInfo();//子类electrician-Worker::printInfo();//父类return 0;
}
多重继承
电工类 继承自 技术工人类技术工人类 继承自 工人类逐级继承超过量层就是 多重继承例子m_strName来自Worker、m_strSkill 来自SkillWorker m_iSalary来自子类独有变量
#include iostreamclass Person{
public:Person(int age);~Person();int getAge();protected:int m_iAge;
};Person::Person(int age) {std::cout Person std::endl;m_iAge age;
}Person::~Person() {std::cout ~Person std::endl;
}int Person::getAge() {return m_iAge;
}
class Worker : public Person{
public:Worker(std::string name,int age);~Worker();std::string getName()const;
protected:std::string m_strName;
};
Worker::Worker(std::string name, int age):Person(age) {m_strName name;std::cout Worker std::endl;
}Worker::~Worker() {std::cout ~Worker std::endl;
}
std::string Worker::getName() const {return m_strName;
}class Electrician : public Worker{
public:Electrician(std::string name, int age, int salary): Worker(name,age){m_iSalary salary;std::cout Electrician std::endl;};~Electrician(){std::cout ~Electrician std::endl;};void printInfo() const;protected:int m_iSalary;
};
int main(void)
{Electrician *electrician new Electrician(jim,30,233333);delete electrician;electrician nullptr;return 0;
}// _ooOoo_ //
// o8888888o //
// 88 . 88 //
// (| ^_^ |) //
// O\ /O //
// ____/---\____ //
// . \\| |// . //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| \---/ | | //
// \ .-\__ - ___/-. / //
// ___. . /--.--\ . . ___ //
// . .___\_|_/___. . //
// | | : - \.;\ _ /;./ - : | | //
// \ \ -. \_ __\ /__ _/ .- / / //
// -.____-.___\_____/___.-____.- //
// --- //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //多继承
多继承是横向的作为一个子类不仅仅拥有一个父类例 在职研究生继承自研究生和职员两个类 两个类都是实体例 水路两栖坦克继承自坦克和 能水中行驶 两个类 其中一个类表示能力、属性(多态)继承的多个类 需要声明属性并且使用 逗号相隔
#include iostreamclass Farmer{
public:Farmer(int age);~Farmer();int getAge();protected:int m_iAge;
};Farmer::Farmer(int age) {std::cout Farmer std::endl;m_iAge age;
}Farmer::~Farmer() {std::cout ~Farmer std::endl;
}int Farmer::getAge() {return m_iAge;
}
class Worker {
public:Worker(std::string name);~Worker();std::string getName()const;
protected:std::string m_strName;
};
Worker::Worker(std::string name) {m_strName name;std::cout Worker std::endl;
}Worker::~Worker() {std::cout ~Worker std::endl;
}
std::string Worker::getName() const {return m_strName;
}class MigrantWorker : public Worker,public Farmer{
public:MigrantWorker(std::string name, int age, int salary):Worker(name),Farmer(age){m_iSalary salary;std::cout MigrantWorker std::endl;};~MigrantWorker(){std::cout ~MigrantWorker std::endl;};void printInfo() const;protected:int m_iSalary;
};
void MigrantWorker::printInfo() const {std::cout m_strName m_iAge m_iSalary std::endl;
}
int main(void)
{MigrantWorker *p new MigrantWorker(001,20,222222);p-printInfo();delete p;p nullptr;return 0;
}
菱形继承
类和类之间的继承关系构成了一个菱形比如 工人类 和 农民类都继承自 人类而农民工类 继承了工人类 和 农民类。四者之间构成了一个菱形为了解决这个问题需要使用虚继承 或者在 头文件引用 #pragma oncePerson 人类Worker 工人Farmer 农民MigrantWorker 农民工工人类 和 农民类 在自己的头文件中引用Person类会多次引用 造成重定义 因此使用 #pragma once 或者#ifndef XXXX 换行 define XXXX 在代码的最末尾输入 #endif农民工 继承的工人和农民都包含了 来自人类的冗余字段即 人类类中的字段 拥有三份分别来自 人类 工人 和农民。因此需要指定 继承的字段来自于哪个特定的类 使用虚继承class MigrantWorker : virtual public Worker 使用virtual将相同的数据成员合并virtual 写在public前后 都可以
注意事项
结构体 也可以使用 继承机制对于父类是基于属性的不是抽象的实体需要对其进行重写面向对象的三大特征 封装 - 继承 - 多态
多态 虚函数理论
虚函数 virtual如果不使用虚函数 virtual子类都会使用父类的方法不会使用自己重新定义的方法
#include iostream
#include cmathclass Shape{
public:Shape(){std::cout Shape std::endl;};double calcArea(){std::cout Shape calcArea std::endl;};
};class Rectangle : public Shape{
public:Rectangle(double width,double height){m_dHeight height;m_dWidth width;std::cout Rectangle std::endl;}double caleArea(){std::cout Rectangle calcArea std::endl;return m_dWidth * m_dHeight;};protected:double m_dWidth;double m_dHeight;
};class Circle : public Shape{
public:Circle(double r){std::cout Circle std::endl;m_iR r;};double caleArea(){std::cout Circle calcArea std::endl;return 3.14 * pow(m_iR,2);}protected:double m_iR;
};int main(void)
{Shape *p1 new Rectangle(2.0,3.0);Shape *p2 new Circle(2.0);p1-calcArea();p2-calcArea();return 0;
} Shape.h
基类中 使用virtual调用的时候将执行子类的方法不再使用父类的方法
#pragma onceclass Shape{
public:Shape();virtual double calcArea();
};Shape::Shape() {std::cout Shape std::endl;
}
double Shape::calcArea() {std::cout Shape calcArea std::endl;return -1;
}
Rectangle.h
#pragma once
#include Shape.hclass Rectangle : public Shape{
public:Rectangle(double width,double height);virtual double calcArea();protected:double m_dWidth;double m_dHeight;
};Rectangle::Rectangle(double width, double height) {m_dHeight height;m_dWidth width;std::cout Rectangle std::endl;
}double Rectangle::calcArea() {std::cout Rectangle calcArea std::endl;return m_dWidth * m_dHeight;
}
Circle.h
#pragma once
#include Shape.h
#include cmathclass Circle : public Shape{
public:Circle(double r);double calcArea();protected:double m_iR;
};Circle::Circle(double r) {std::cout Circle std::endl;m_iR r;
}double Circle::calcArea(){std::cout Circle calcArea std::endl;return 3.14 * pow(m_iR,2);
}
main.c
#include iostream
#include include/Shape.h
#include include/Rectangle.h
#include include/Circle.hint main(void)
{Shape *p1 new Rectangle(2.0,3.0);Shape *p2 new Circle(2.0);p1-calcArea();p2-calcArea();return 0;
}
虚析构函数
父类指针 指向子类对象的副作用虚析构函数Shape *p1 new Rectangle(2.0,3.0); Shape *p2 new Circle(2.0); delete p1或者p2删除他们指向的内存空间但是 如果Rectangle或者CIrcle内部在堆上 申请的内存 无法通过父类指针 p1 p2 释放内部的内存空间造成内存的泄露需要定义 虚析构函数子类定义自己的析构函数否则自己申请的内存释放定义析构函数之后 delete p1;就会执行子类的析构函数
#include iostream
#include cmathclass Shape{
public:Shape();virtual ~Shape();virtual double calcArea();
};Shape::Shape() {std::cout Shape std::endl;
}Shape::~Shape() {std::cout ~Shape std::endl;
}double Shape::calcArea() {std::cout Shape calcArea std::endl;return -1;
}class Rectangle : public Shape{
public:Rectangle(double width,double height);virtual double calcArea();~Rectangle();
protected:double m_dWidth;double m_dHeight;int *m_pArr;
};Rectangle::Rectangle(double width, double height) {m_dHeight height;m_dWidth width;std::cout Rectangle std::endl;m_pArr new int[20];
}double Rectangle::calcArea() {std::cout Rectangle calcArea std::endl;return m_dWidth * m_dHeight;
}Rectangle::~Rectangle() {delete []m_pArr;std::cout ~Rectangle std::endl;
}class Circle : public Shape{
public:Circle(double r);double calcArea();~Circle();
protected:double m_iR;
};Circle::~Circle() {std::cout ~Circle std::endl;
}
Circle::Circle(double r) {std::cout Circle std::endl;m_iR r;
}double Circle::calcArea(){std::cout Circle calcArea std::endl;return 3.14 * pow(m_iR,2);
}int main(void)
{Shape *p1 new Rectangle(2.0,3.0);Shape *p2 new Circle(2.0);p1-calcArea();p2-calcArea();delete p1;delete p2;p1 nullptr;p2 nullptr;return 0;
}
虚函数的实现原理
虚函数表只放 虚函数指针虚析构函数 先调用子类的析构函数如果父类的析构函数存在再调用父类的析构函数。两者之间使用链表结构衔接任何类型的指针 都占据 4个字节 用于验证虚函数 表指针的存在 (配合sizeof使用)纯虚函数
基类 virtual double calcArea() 0; 纯虚函数没有函数体当子类继承 基类的纯虚函数 需要对纯虚函数的内容进行 填写含有纯虚函数的类叫做 抽象类Shape *p new Shape(); //抽象类不可以实例化抽象类不可以new出对象因为对象无法表达内部的函数行为纯虚函数本质基于纯虚函数的子类也可以是子类性质一致直到可以形成对象抽象类中只有纯虚函数呢接口类
纯虚函数 抽象类
纯虚函数的改正 virtual double calcArea() 0;删除先前的基类对其 的定义
#include iostream
#include cmathclass Shape{
public:Shape();virtual ~Shape();virtual double calcArea() 0;
};Shape::Shape() {std::cout Shape std::endl;
}Shape::~Shape() {std::cout ~Shape std::endl;
}class Rectangle : public Shape{
public:Rectangle(double width,double height);virtual double calcArea();~Rectangle();
protected:double m_dWidth;double m_dHeight;int *m_pArr;
};Rectangle::Rectangle(double width, double height) {m_dHeight height;m_dWidth width;std::cout Rectangle std::endl;m_pArr new int[20];
}double Rectangle::calcArea() {std::cout Rectangle calcArea std::endl;return m_dWidth * m_dHeight;
}Rectangle::~Rectangle() {delete []m_pArr;std::cout ~Rectangle std::endl;
}class Circle : public Shape{
public:Circle(double r);double calcArea();~Circle();
protected:double m_iR;
};Circle::~Circle() {std::cout ~Circle std::endl;
}
Circle::Circle(double r) {std::cout Circle std::endl;m_iR r;
}double Circle::calcArea(){std::cout Circle calcArea std::endl;return 3.14 * pow(m_iR,2);
}int main(void)
{Shape *p1 new Rectangle(2.0,3.0);Shape *p2 new Circle(2.0);p1-calcArea();p2-calcArea();delete p1;delete p2;p1 nullptr;p2 nullptr;return 0;
}
仅仅含有纯虚函数的类 叫做接口类在接口类的基础上 实现部分纯虚函数叫做抽象类 但是没有实现全部 不可以定义对象只有全部实现父类 之路上所有的虚函数才可以定义对象抽象类 和 接口类的用途 抽象类用于表达不完全的实体概念接口类用于表达一种强制协议或者能力
C 数据类型装换
reinterpret_castnew_type(expression) 将指针和引用的数据类型进行转换dynamic_casttype(expression) 父类和子类数据之间的转换 只有多态才可以采用static_casttype(expression) 普通数据类型的转换const_casttype(expression) const 和 非const之间的转换
#include iostreamclass Shape{
public:Shape();virtual ~Shape();virtual double calcArea();protected:double i_mNumber;
};class Circle : public Shape{
public:Circle(double r);~Circle();
protected:double m_iR;
};
int main(){double x 2.5;//static_castint y (int)x;//C语言int z static_castint(x);//C//dynamic_castShape *p new Circle(2.0);Circle *q dynamic_castCircle *(p);//reinterpret_castint r 1000;int *w reinterpret_castint *(r);//将数值转化为地址 很危险//const int c 0;
// int *o x;//Errorint *g const_castint *(c);
}RTTI
C中的RTTI机制RTTI的主要表现形式是 type_idRTTI的前提是 父类具有虚函数
异常处理
异常 程序运行期间出现的问题或者错误异常处理处理异常的方法在有可能发生异常的地方做出预见性的安排。异常处理提供了处理程序运行时出现的任何意外或者异常情况的方法C 处理异常基本思想异常 的检测和处理进行分离遇到异常 1遇到错误立即终止程序2返回一个表示错误的数值同时保留错误的信息3针对错误情况再次分析处理throwtry catchcatch(...) 捕捉所有的异常
#include iostreamvoid fun1(){std::cout fun1 std::endl;throw wer;
}void fun2(){try {fun1();} catch (int e1){std::cout exception int: e1 std::endl;} catch (double e2) {std::cout exception double: e2 std::endl;} catch (...) {std::cout 全部捕捉! std::endl;}std::cout fun2 std::endl;
}
int main(){fun2();
}异常的组织形式 平行机构通过枚举来组织异常1优点语义相关的错误放在一起条理清晰适合一些简单的带有初始值的错误信息2缺点无法详细的描述异常的类型树形结构通过异常的层次关系来组织异常1优点可以在类的内部详细的描述错误的类型2缺点容易造成类层次的无限扩充反而不容易理解
#include iostream#define ERR_OK 0x0000
#define ERR_OK_MSG OK#define ERR_CARD 0x8101
#define ERR_CARD_MSG No Card#define ERR_UNKnown 0x8000
#define ERR_UNK_MSG unknown errorenum MathException{ZeroException 10,NegativeException 20,NoSolutionException 30
};class MyException{
public:MyException(int code,std::string msg);virtual int getErrorInfo(std::string msg);protected:int m_iCode;std::string m_strMsg;
};
MyException::MyException(int code, std::string msg) {std::cout MyException std::endl;m_iCode code;m_strMsg msg;
}int MyException::getErrorInfo(std::string msg) {std::cout getErrorInfo std::endl;msg m_strMsg;return m_iCode;
}
class HardwareMyException : public MyException{
public:HardwareMyException(int code,std::string msg);virtual int getErrorInfo(std::string msg);
};
HardwareMyException::HardwareMyException(int code, std::string msg) :MyException(code,msg) {std::cout HardwareMyException std::endl;
}
int HardwareMyException::getErrorInfo(std::string msg) {std::cout HardwareMyException getErrorInfo std::endl;msg m_strMsg;return m_iCode;
}void fun1(int flag){switch (flag) {case 0:throw MyException(ERR_CARD,ERR_CARD_MSG);break;case 1:throw MyException(ERR_UNKnown,ERR_UNK_MSG);break;case 2:throw HardwareMyException(ERR_OK,ERR_OK_MSG);break;}
}
int main(){try {fun1(1);} catch (MyException e) {std::string msg;std::cout e.getErrorInfo(msg) std::endl;std::cout msg std::endl;}
}运算符号重载
运算符重载的方式1成员函数重载 2友元函数重载一元运算符重载 负号(-) 递增运算符号() 递减运算符号(--)二元运算符重载 加减号(-) 索引符号[] 输出流 不可以重载运算符号 . .* :: ?: {} sizeof
成员函数重载
#includeiostream
using namespace std;class Binomial{
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator-();
private:int m_iA;int m_iB;
};
int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}
Binomial Binomial::operator-() {this-m_iA -(this-m_iA);m_iB -m_iB;return *this;//返回对象
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(0,0);bin2 -bin1;std::cout bin1.getA() std::endl;std::cout bin1.getB() std::endl;std::cout bin2.getA() std::endl;std::cout bin2.getB() std::endl;return 0;
}友元函数重载 this指针失效
#includeiostream
using namespace std;class Binomial{friend Binomial operator-(Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();
private:int m_iA;int m_iB;
};int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}
//Binomial Binomial::operator-() {
// this-m_iA -(this-m_iA);
// m_iB -m_iB;
// return *this;//返回对象
//}
Binomial operator-(Binomial bin){bin.m_iA -bin.m_iA;bin.m_iB -bin.m_iB;return bin;
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(0,0);bin2 -bin1;std::cout bin1.getA() std::endl;std::cout bin1.getB() std::endl;std::cout bin2.getA() std::endl;std::cout bin2.getB() std::endl;return 0;
}递增运算符重载
JJ
#includeiostream
using namespace std;class Binomial{friend Binomial operator-(Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 j
private:int m_iA;int m_iB;
};int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}
//Binomial Binomial::operator-() {
// this-m_iA -(this-m_iA);
// m_iB -m_iB;
// return *this;//返回对象
//}
Binomial operator-(Binomial bin){bin.m_iA -bin.m_iA;bin.m_iB -bin.m_iB;return bin;
}
Binomial Binomial::operator() {m_iA;m_iB;return *this;
}
Binomial Binomial::operator(int) {Binomial old(*this);m_iA;m_iB;return old;
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(0,0);
// bin2 -bin1;bin1;std::cout bin1.getA() std::endl;std::cout bin1.getB() std::endl;// std::cout bin2.getA() std::endl;
// std::cout bin2.getB() std::endl;return 0;
}二元运算符重载 成员函数重载
#includeiostream
using namespace std;class Binomial{friend Binomial operator-(Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 jBinomial operator(Binomial bin);
private:int m_iA;int m_iB;
};int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}
//Binomial Binomial::operator-() {
// this-m_iA -(this-m_iA);
// m_iB -m_iB;
// return *this;//返回对象
//}
Binomial operator-(Binomial bin){bin.m_iA -bin.m_iA;bin.m_iB -bin.m_iB;return bin;
}
Binomial Binomial::operator() {m_iA;m_iB;return *this;
}
Binomial Binomial::operator(int) {Binomial old(*this);m_iA;m_iB;return old;
}Binomial Binomial::operator(Binomial bin) {Binomial temp(0,0);temp.m_iA m_iA bin.m_iA;temp.m_iB m_iB bin.m_iB;return temp;
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(8,-5);Binomial bin3(0,0);bin3 bin1 bin2;std::cout bin1.getA() std::endl;std::cout bin1.getB() std::endl;std::cout bin2.getA() std::endl;std::cout bin2.getB() std::endl;std::cout bin3.getA() std::endl;std::cout bin3.getB() std::endl;return 0;
}
//5
//3
//8
//-5
//13
//-2友元类型
#includeiostream
using namespace std;class Binomial{friend Binomial operator-(Binomial bin);friend Binomial operator(Binomial bin1,Binomial bin2);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 j
// Binomial operator(Binomial bin);
private:int m_iA;int m_iB;
};int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}
//Binomial Binomial::operator-() {
// this-m_iA -(this-m_iA);
// m_iB -m_iB;
// return *this;//返回对象
//}
Binomial operator-(Binomial bin){bin.m_iA -bin.m_iA;bin.m_iB -bin.m_iB;return bin;
}
Binomial Binomial::operator() {m_iA;m_iB;return *this;
}
Binomial Binomial::operator(int) {Binomial old(*this);m_iA;m_iB;return old;
}Binomial operator(Binomial bin1,Binomial bin2){Binomial temp(0,0);temp.m_iA bin1.m_iA bin2.m_iA;temp.m_iB bin1.m_iB bin2.m_iB;return temp;
}
//Binomial Binomial::operator(Binomial bin) {
// Binomial temp(0,0);
// temp.m_iA m_iA bin.m_iA;
// temp.m_iB m_iB bin.m_iB;
// return temp;
//}int main(void)
{Binomial bin1(5,3);Binomial bin2(8,-5);Binomial bin3(0,0);bin3 bin1 bin2;std::cout bin1.getA() std::endl;std::cout bin1.getB() std::endl;std::cout bin2.getA() std::endl;std::cout bin2.getB() std::endl;std::cout bin3.getA() std::endl;std::cout bin3.getB() std::endl;return 0;
}
//5
//3
//8
//-5
//13
//-2重载
只可以使用友元函数重载 不可以使用成员函数因为第一个必须是cout类型
#includeostream
#include iostreamclass Binomial{friend Binomial operator-(Binomial bin);friend Binomial operator(Binomial bin1,Binomial bin2);friend std::ostream operator (std::ostream out,Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 j
// Binomial operator(Binomial bin);
private:int m_iA;int m_iB;
};int Binomial::getA() {return m_iA;
}
int Binomial::getB() {return m_iB;
}
Binomial::Binomial(int a, int b) {m_iA a;m_iB b;
}
Binomial::~Binomial() {}Binomial operator-(Binomial bin){bin.m_iA -bin.m_iA;bin.m_iB -bin.m_iB;return bin;
}
Binomial Binomial::operator() {m_iA;m_iB;return *this;
}
Binomial Binomial::operator(int) {Binomial old(*this);m_iA;m_iB;return old;
}Binomial operator(Binomial bin1,Binomial bin2){Binomial temp(0,0);temp.m_iA bin1.m_iA bin2.m_iA;temp.m_iB bin1.m_iB bin2.m_iB;return temp;
}std::ostream operator (std::ostream out,Binomial bin){out bin.m_iA x ( bin .m_iB );return out;
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(8,-5);Binomial bin3(0,0);bin3 bin1 bin2;std::cout bin1 std::endl;//operator(cout , bin)std::cout bin2 std::endl;//operator(cout , bin2)std::cout bin3 std::endl;//operator(cout , bin3)return 0;
}
索引运算符重载
一元函数运算 5x 35为a 3为b预期通过 bin[0] 得到a通过bin[1] 得到b
#includeostream
#include iostreamclass Binomial{friend Binomial operator-(Binomial bin);friend Binomial operator(Binomial bin1,Binomial bin2);friend std::ostream operator (std::ostream out,Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 jint operator[](unsigned int i);
// Binomial operator(Binomial bin);
private:int m_iA;int m_iB;
};int Binomial::operator[](unsigned int i) {if (i 0){return m_iA;} else if (i 1){return m_iB;} else{throw 1;}
}
int main(void)
{Binomial bin1(5,3);std::cout bin1[0] std::endl; //bin.operator[](0)std::cout bin1[1] std::endl; //bin.operator[](1)return 0;
}
等于重载
#includeostream
#include iostreamclass Binomial{friend Binomial operator-(Binomial bin);friend Binomial operator(Binomial bin1,Binomial bin2);friend std::ostream operator (std::ostream out,Binomial bin);
public:Binomial(int a,int b);~Binomial();int getA();int getB();Binomial operator();//前置 jBinomial operator(int);//后置 jint operator[](unsigned int i);bool operator(Binomial bin);
// Binomial operator(Binomial bin);
private:int m_iA;int m_iB;
};bool Binomial::operator(Binomial bin) {if (this-m_iA bin.m_iA this-m_iB bin.m_iB){return true;}else{return false;}
}
int main(void)
{Binomial bin1(5,3);Binomial bin2(4,5);if (bin1 bin2){std::cout A std::endl;}else{std::cout B std::endl;}return 0;
}运算符重载
注意事项
若一个运算的操作需要修改对象的状态选择重载为成员函数也可以使用友元函数修改对象的状态当运算符号所需要的操作数(尤其是第一个操作数)希望有隐式类型转换则只能使用友元函数当需要重载运算符号具有可交换性选择重载为友元函数