C++ 查漏补缺

特性关系

  • C语言面向过程
  • C++面向过程 + 面向对象(封装 继承 多态)
  • C++具备C语言的全部特性的基础上,并且支持更多新的特性

内存泄露

  • 申请内存,没有释放
  • 申请 malloc new
  • 释放 free delete
  • ProcessExplorer查看内存是否释放

代码移植

  • 将生成的exe运行在别的平台,不因为缺乏环境而出错
  • 使用软件 ProcessExplorer软件查找exe程序执行需要的dll等配置文件
  • 或者在程序编译的时候整合对应的库文件,(属性->C/C++ -> 代码生成 ->运行库)
  • 多线程 MT                    静态编译 -> 链接库文件
  • 多线程调试 MTH          静态编译 -> 链接库文件
  • 多线程 DLL MD
  • 多线程调试DLL MDT

pragma once

  • 确保.h文件减少包含,减少编译错误

C++输入输出方式

  • 输入  输入设备-> 输入流->scanf/cin -> 变量    cin>>a>>b;
  • 输出  变量 ->  printf/cout -> 输出流 -> 输出设备  cout<< a << endl;(endl输出换行)
  • 不需要关注 数据类型 和 占位符号

命名空间

  • std 
  • 关键字 namespace ,不同的命名空间使用相同的变量名字和函数
  • 使用  命名空间:: 函数名  进行函数的调用

数据类型

  • 布尔类型 bool 
  • 真 true
  • 假 false
  • bool 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                 释放内存 delete
  • int *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 = 0;
  • const 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; x=10;错误
  • const修饰指针类型    
  • int const *p = &x; const修饰*,因此p可以变化,但是*p 不可以
  • int * const p = &x;const 修饰p,因此*p可以变化 但是p不可以
    //const 修饰指针类型int x=0,y=0;int const *p = &x;*p = 4;//错误p = &y;//正确
    //const 修饰指针类型int x=0,y=0;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  结构体的默认访问权限是public
  • struct结构体的使用和函数类似,也可以使用上述 堆和栈的方式进行定义
  • Student *p = new Student[20];//定义20个学生变量,p[0].age = 10;控制第一个学生的age变量  使用delete[] p删除内存空间
#include <iostream>
#include <fstream>
#include <sstream>struct 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 <sstream>class 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 <sstream>class 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 <sstream>class 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 <sstream>class 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 <iostream>class 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 <iostream>class 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 <iostream>class 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 <iostream>class 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.h"void 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;
}

友元成员函数  

#include<iostream>
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 <iostream>class 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 <iostream>class 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 <iostream>class 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 <iostream>class 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 once
  • Person               人类
  • Worker               工人
  • Farmer               农民
  • MigrantWorker   农民工
  • 工人类 和 农民类  在自己的头文件中引用Person类,会多次引用 造成重定义 ,因此使用  #pragma once  或者#ifndef XXXX 换行 define XXXX 在代码的最末尾输入 #endif
  • 农民工 继承的工人和农民都包含了 来自人类的冗余字段,即 人类类中的字段 拥有三份,分别来自  人类  工人  和农民。因此需要指定 继承的字段来自于哪个特定的类  使用虚继承
  • class MigrantWorker : virtual public Worker   使用virtual,将相同的数据成员合并
  • virtual 写在public前后 都可以

注意事项

  • 结构体 也可以使用 继承机制
  • 对于父类是基于属性的,不是抽象的实体,需要对其进行重写
  • 面向对象的三大特征 封装 -> 继承 -> 多态

多态 虚函数理论

  • 虚函数  virtual
  • 如果不使用虚函数 virtual,子类都会使用父类的方法,不会使用自己重新定义的方法
#include <iostream>
#include <cmath>class 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.h"class 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 <cmath>class 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.h"int 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 <cmath>class 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 <cmath>class 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_cast<new_type>(expression)    将指针和引用的数据类型进行转换
  • dynamic_cast<type>(expression)                父类和子类数据之间的转换   只有多态才可以采用
  • static_cast<type>(expression)                     普通数据类型的转换
  • const_cast<type>(expression)                     const 和 非const之间的转换
#include <iostream>class 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_cast<int>(x);//C++//dynamic_castShape *p = new Circle(2.0);Circle *q = dynamic_cast<Circle *>(p);//reinterpret_castint r = 1000;int *w = reinterpret_cast<int *>(r);//将数值转化为地址 很危险//const int c = 0;
//    int *o = &x;//Errorint *g = const_cast<int *>(&c);
}

RTTI 

  • C++中的RTTI机制
  • RTTI的主要表现形式是 type_id
  • RTTI的前提是 父类具有虚函数

异常处理

  • 异常 程序运行期间出现的问题或者错误
  • 异常处理:处理异常的方法在有可能发生异常的地方做出预见性的安排。异常处理提供了处理程序运行时出现的任何意外或者异常情况的方法
  • C++ 处理异常基本思想:异常 的检测和处理进行分离
  • 遇到异常 
  • 1,遇到错误,立即终止程序
  • 2,返回一个表示错误的数值,同时保留错误的信息
  • 3,针对错误情况再次分析处理
  • throw
  • try catch
  • catch(...) 捕捉所有的异常
#include <iostream>void 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 error"enum 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

成员函数重载

#include<iostream>
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指针失效

#include<iostream>
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;
}

递增运算符重载

  • ++J
  • J++
#include<iostream>
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;
}

 二元运算符重载

  • +  
  • 成员函数重载  
#include<iostream>
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++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 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

友元类型

#include<iostream>
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类型
#include<ostream>
#include <iostream>class 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 + 3;5为a 3为b
  • 预期通过 bin[0] 得到a,通过bin[1] 得到b
#include<ostream>
#include <iostream>class 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++int 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;
}

==等于重载

#include<ostream>
#include <iostream>class 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++int 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;
}

=运算符重载

注意事项

  • 若一个运算的操作需要修改对象的状态,选择重载为成员函数,也可以使用友元函数修改对象的状态
  • 当运算符号所需要的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能使用友元函数
  • 当需要重载运算符号具有可交换性,选择重载为友元函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446587.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++面向对象高级编程 学习十六 vptr和vtbl

当一个类中有一个或多个虚函数时&#xff0c;内存中会多一个虚指针&#xff08;vptr&#xff0c;virtual pointer&#xff09;&#xff0c;指向一个虚表&#xff08;vtbl&#xff0c;virtual table&#xff09; 父类有虚函数&#xff0c;则子类一定有虚函数 在下图示意图中&a…

英语口语Week16 Thursday

英语文章 It is an impossibility that everything runs smoothly in everyday life. Where there is trouble, there could be anxiety.Anxiety is a common phenomenon; you are not the only one carrying it. But, it could be somewhat poisonous if you don’t let it o…

static内容相关介绍学习

说一下static关键字的作用 当程序执行到函数内部定义的变量时&#xff0c;编译器为它在栈上分配空间&#xff0c;函数在栈上分配的空间在此函数执行结束时会释放掉&#xff0c;这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时&#xff0c;如何实现&#xf…

C++STL与泛型编程(2) 第一个C++ STL Application

文章目录STL六大部件STL六大部件代码示例时间复杂度前闭后开区间auto关键字的用法STL六大部件 容器 分配器 算法 迭代器 适配器 仿函数 容器要放东西&#xff0c;东西要占用内存&#xff0c;分配器可支持容器解决内存问题。算法处理容器中的数据。迭代器是容器和算法之间的桥…

C++STL与泛型编程(3)容器之分类与测试

文章目录容器的分类序列式容器&#xff08;sequence containers&#xff09;代码示例辅助函数array 容器array容器的测试代码测试代码中部分函数解析vector 容器vector 容器的测试代码测试代码中部分函数解析list 容器list 容器的测试代码测试代码中部分函数解析forward_list 容…

C++面试题目

C和C的区别 总览 C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得到输出&#xff08;或实现过程&#xff08;事务&#xff09;控制&#xff09;。C&…

C++STL与泛型编程(4)OOP(面向对象编程) Vs. GP(泛型编程)

文章目录OOP和GP为什么list不能使用标准库中的::sort算法&#xff1f;采用GP的好处OOP和GP OOP将datas和methods关联在一起 GP将datas和methods分开 为什么list不能使用标准库中的::sort算法&#xff1f; 因为标准库的sort的算法用到了随机访问迭代器&#xff08;RandomAcce…

牛客网 链表结构 算法相关内容

链表结构 单链表的节点结构 由以下结构的节点依次连接起来所形成的链叫单链表结构 Clas Node<V>{ V value; Node next; } 双链表的节点结构 由以下结构的节点依次连接起来所形成的链叫双链表结构 Clas Node<V>{ V value; Node next; Node last; } 单链表和双…

C++ primer 第8章 IO库

文章目录IO库类型和头文件IO对象无拷贝或赋值IO流的条件状态文件输入输出ifstream 示例ofstream 示例文件模式以out模式打开文件会丢弃已有数据每次调用open时都会确定文件模式ofstream 保留源文件 追加数据 示例string流istringstream示例ostringstream示例IO库类型和头文件 …

C++面试宝典 基本语言(三)

如果同时定义了两个函数&#xff0c;一个带const&#xff0c;一个不带&#xff0c;会有问题吗&#xff1f; 不会&#xff0c;这相当于函数的重载 #include<iostream> class A{ public:void print()const{std::cout << "Hello" << std::endl;}void…

C++ primer 第9章 顺序容器

文章目录顺序容器类型确定使用哪种顺序容器容器库概览容器操作迭代器迭代器支持的所有操作迭代器支持的所有运算迭代器范围对构成范围的迭代器的要求标准库迭代器范围左闭右开的三种性质容器定义和初始化将一个新容器创建为另一个容器的拷贝将array拷贝到vector中的代码与顺序容…

牛客网C++面经 容器和算法

原文网址 参考网址 C语言中文网 请你来说一下map和set有什么区别&#xff0c;分别又是怎么实现的&#xff1f; map和set都是C的关联容器&#xff0c;其底层实现都是红黑树&#xff08;RB-Tree&#xff09;。由于 map 和set所开放的各种操作接口&#xff0c;RB-tree 也都提供…

C++ primer 第10章 泛型算法

文章目录概述findcount初识泛型算法只读算法只读算法accumulate只读算法equal写容器元素的算法算法fill算法fill_nback_inserter算法copy算法replace replace_copy重排容器元素的算法sortuniqueunique_copy定制操作向算法传递函数谓词算法stable_sort算法partitionlambda表达式…

C语言常用字符串函数

概括 代码 #include<stdlib.h> #include<stdio.h> #include<string.h> int main() {//常用字符串函数char a[]"abcSDFbnm";char b[]"SD";printf("a的字符串长度:%d\n",strlen(a));printf("b的字符串长度:%d\n",str…

C++ primer 第11章 关联容器

文章目录使用关联容器map示例关联容器概述定义关联容器关联容器值初始化multimap和multiset关键字类型的要求pair类型pair上的操作关联容器操作关联容器额外的类型别名关联容器迭代器map迭代器set迭代器关联容器和算法添加元素向map添加元素检测insert的返回值使用insert代替下…

C++ primer 第12章 动态内存

文章目录前言动态内存与智能指针shared_ptr类shared_ptr和unique_ptr都支持的操作shared_ptr独有的操作make_shared 函数shared_ptr的拷贝和赋值shared_ptr自动销毁所管理的对象shared_ptr还会自动释放相关联的内存程序使用动态内存出于以下原因直接管理内存使用new动态分配和初…

C语言顺序查找二分查找

介绍 顺序查找 按照顺序一个个查找 #include<stdio.h> //顺序查找 int search(int arr[],int len,int aim) {int i;for(i0;i<len;i){if(arr[i]aim){return i;//返回下标 }}return -1;//表示未查询到} int main() {int arr[]{13,355,256,65,234,-1,35,-6,-3,-4,0};…

C++ primer 第12章 12.3 使用标准库:文本查询程序

文章目录使用标准库&#xff1a;文本查询程序文本查询程序设计数据结构在类之间共享数据自己的文本查询程序书中的文本查询程序使用标准库&#xff1a;文本查询程序 我们将实现一个简单的文本查询程序&#xff0c;作为标准库相关内容学习的总结。 我们的程序允许用户在一个给…

C语言二维数组 int arr[2][3]

基础使用 先遍历行再遍历列 #include<stdio.h> //二维数组的基本使用 int main() {//二维数组的初始化int arr1[2][2]{{2,2},{0,0}};int arr2[2][3]{2,2,2,8,8,8};int arr3[6][9];int i,j;for(i0;i<6;i){for(j0;j<9;j){arr3[i][j]1;}}arr3[2][5]0;//打印printf(&…

C++ primer 第13章 拷贝控制

文章目录前言拷贝、赋值与销毁拷贝构造函数合成拷贝构造函数拷贝初始化和直接初始化拷贝初始化的发生&#xff1a;参数和返回值拷贝初始化的限制拷贝赋值运算符重载赋值运算符合成拷贝赋值运算符析构函数析构函数完成的工作什么时候会调用析构函数合成析构函数代码片段调用几次…