C++复习笔记

1 篇文章 0 订阅

C++ 复习思维导图
在这里插入图片描述
云笔记传送门

引言

  • 如何在C++中调用C的函数
    • 编译->链接
    • 连接错误:缺了?多了?
    • Missing:C与C++的修辞功能不同
    • extern “C” void c_fun()
  • myc.h 中如何声明,使得include不会出错?
    #ifdef __cplusplus  //预定义的宏,用C++兼容
    extern "C"
    {
        ...
    }
    #endif
    
  • Library
    • dynamic .dll 动态链接库
    • static .lib 参与原程序的生成,被.exe包含
    • 构建库
      • 没有main()
      • 编译生成.lib
    • 使用库
      • 拷贝到工程文件夹下
      • 工程配置倒入库
      #pragma comment(lib,"testlib.lib")
      

万物皆对象

对其成员/属性(函数、变量)进行保护
struct 默认公有
class 默认私有
对象就是一段连续的内容

class Student {
    private:
        int age;
    public:
        void init();
}
Student s1; //8个字节的前4个是age
int *p = (int *)&s1;
*p = 30;

封装

分治————访问控制

  • 运行内存 sizeof() (函数不占)
  • 加载内存
//按照大字节对齐来分配内存
int i,j;
char c1,c2; //12

double d;
int i;
char c;  // 24

三无

  • 语言无关
  • 平台无关
  • 应用无关

并发的解决思路

  • 并转串:根据用户量构建缓冲区,服务器处理(实时要求低)
  • 多机:入口机分配任务给其他处理机

构造,析构,拷贝

  • 理想的分工方式:分层(库程序员,中端~,…)
  • 实际上:按模块,有重复
    • 正太:自然
    • 幂律:人为
  • 声明时隐式构造:
Student s(10,"liu");
Student s;//没有参数,则不要加括号,调用的是无参数构造
Student s();//否则为函数声明
Student(); //人为不定义时默认构造器,一旦定义就不再存在:声明时不得不传参数
  • 默认参数构造:Test(int i,int j=0)性能低,不建议
  • 程序运行空间:
    • 静态: 常量,代码区
    • Runtime: global
      • stack : 局部变量————不同寻址(快),但生命周期短
      • heap: 动态分配空间————new
  • handle: (某类中)
    • Student s;//必要组成部分 value
    • Student *s; //不必要 handle
  • int 的构造函数:
    j =new int(aj);
    int i(10);
    
  • 析构:
    • 对象消失时(对象所在函数调用完毕)自动调用
    • 无条件(参数),唯一
      • 没有显示析构时,编译系统会自动生成缺省的析构
      • 定义类析构,则先调用自定义再调用合成(无操作)
    • ~Test();
    • 只要构造有new,一定有析构,释放一切空间
    • 数据成员:属性/通信(函数交换信息)
    • D内存泄漏:handle丢失,但对象未被释放
      在这里插入图片描述
    • A释放出错
    Test(int aa){
        i=aa; //如果只初始化i,则j为野指针,释放会出错
        j=NULL;//需要这一步!!!
    }
    

引用 reference(安全的指针)

```
int i=10;
int& r=i;   //引用必初始化:r是i的引用
r++; //i++;
r=j; //i=j;一旦绑定用不分离
```
  • 函数传递引用(地址)
  • pointer:ugly but clear
  • reference: simple but arduous

克隆构造 copy constructor

Test t1 =new Test(1,2);
Test t2(t1);
  • B浅拷贝 bitwise copy
    • 完全一样
    • 类中默认构造 memcpy
      在这里插入图片描述
      t2先死,delete 2,但t1死的时候也会delete,C报错
    • 任意指针不能直接free p,应该if(p!=NULL){free p;p=NULL}
  • 深拷贝 logical copy
Test(Test& t){
    i = t.i;
    j=new int(*t.j);
}
  • E返回局部变量地址

static

static对象可见域生命周期性质
局部变量函数内部同全局变量(main后释放)定义一次后不再构造,直到程序结束死亡
全局函数仅本文件可见static void fun()
数据成员本类所有对象初始化不由构造函数,显式在类外似全局变量一次初始化Test::i=0;
函数成员本类所有对象函数调用不由创建对象;为了调用static数据成员;调用了非static成员则不能加static
  • 程序通讯

    • 函数调用
    • 数据成员
    • static数据成员
    • 全局变量
  • 全局变量定义在源文件中,头文件两次include相当于重定义

  • extern(外连接)会在所有源文件查找,多个则重定义。与static相反

  • 名空间 namespace

    • 解决大型工程重名问题
    namespace T1{
        class ABC{
            
        }
    }
    using namespace T1;
    or T1::ABC a;
    
  • 传值为传拷贝,会调用拷贝构造

  • Never pass by value(大对象)

    • built-in type(原生)
    • defined type
    • self-defined type
  • void dun(const Test *t) 传递指针常量,不可更改

    • 只读
    • 规定只要没有const就要写
  • inline 内联函数

    • 函数定义或声明前加inline(定义必加)
    • 用来定义一个类的~,替代C中表达式的宏定义:高效、审查、私有
    • 用在函数内容简单时候
    • C的宏定义
      • 使用预处理器实现,没有参数压栈,代码生成,效率很高。
      • 但没有C++编译器审查,返回值不能强制转换
      • 涉及到私有,不能用它实现
  • 常量函数————常量对象调用

    • int getI() const{return i;}
    • 前提:不能对该对象修改setI(i)
  • 单件模式:

    • 希望某个类只有一个实例可以被访问
    • 全局对象:
      • 程序质量降低
      • 不能实例化多次
      • 不能保证只有一个实例
    class Scheduler {
        schedule(){}
        static Scheduler *self = NULL;
        public:
        static void get_instance(){
            if(self==NULL) self = new Schedule();
            return self;
        }
    }
    

运算符重载:

  1. 非必需
  2. 与认知相符(如字符串相加)
    a.save(100)–> a=a+100 (Account a)
Account operator+(int money){
    balance+=money;
    return *this;
}
  • 重载运算符:+ - * / [ ] << new
  • new/delete
    • delete = destructor + free
    • new = malloc +constructor
  • malloc/free
    • 预先不知道申请大小,来一个分配一个
    • 动态放堆区:堆>>栈
    • 人为控制生命周期:
    AutoAC ac;//栈区,无法控制
    new AutoAC();//析构delete
    Memory *p = new Memory;
    Memory *p = (*Memory)malloc(sizeof(Memory));
    

继承

  • 内涵:组合
  • 外延:继承
  • 两个类的共性:抽象为同类
class Teacher: public Borrower {
    void borrow() {
        Borrower::borrow(); //完美继承,肯定父类
        cout<<"5 books"<<endl;
    }
}

代码重用 reuse

  • 子类不能使用父类的私有对象。protected 可以
  • 父类private:子类削弱了父类
  • 构造:先父后子(默认构造)
  • 析构:先子后父
Cat():Pet(2,"cat")
{
    cout<<"cat"<<endl;//括号外调用
}
Cat(age,name,type):Pet(age,name),type(type){}//面向对象构造

创建Car,一定会调用Engine()

Car():e(1){
    cout<<"car"<<endl;
}
  • 按照声明顺序执行构造:
Test(int a):j(a),i(j){} : i=j野值;j=a;
  • 私有继承
class Cat:(private) Pet //削弱了父类接口,方法变为私有

多继承

class Base1 {
    void f();
}
class Base2 {
    void h();
    void f();
}
  • ambiguous
  • 用组合代替多继承

多态

子类需要承认共性,再加入特性

多态性

  • 靠虚表实现————有损性能
  • upcasting 向上类型转换 pet.speak();<–cat
  • 子类型不能削弱父类接口
  • binding 绑定
    • 将标识符(函数、变量)转换为地址的过程
    • early binding(static)
      • 编译器/链接器能够直接关联
      • 所有功能有唯一的地址
    • later binding(runtime,dynamic)
      • 只有在运行时才知道该调用哪个
      • 使用函数指针(间接函数调用)
      int add(int x,int y){return x+y;}
      int main(){
          int (*pFcn)(int,int)=add;
          cout<<pFcn(5,3)<<endl;
      }
      
      • 效率低下
  • 虚函数
Pet:virtual void speak();//自动继承
Needle(Pet & pet);
Needle(Pet pet);//调用了拷贝构造,指向Pet,不会调用子类的speak
  • 虚函数表(v-table)
    • 虚指针(v-ptr):在类中占用4个字节
    • 每个使用虚函数的类都有虚拟表
    • 在编译时形成静态数组,存放函数指针,指向该类可访问的最派生函数
      在这里插入图片描述
    • 虚指针由构造函数隐式完成

纯虚函数(仅适用于抽象类:不能创建实例)

virtual void speak() =0

世界上存在Cat,Dog 不存在Pet
子类不去实现则仍为抽象类
意义:

  • 传的是引用,而不是实例
  • 纲领
  • 顶层抽象设计,规定所有子类的行为
  • 可以有函数体,子类调用

Interface

class Machine()
class Airplane() : public Machine, public **FlyObject**        void fly();
class Bird(): public Animal, public **FlyObject**              void fly();
class Radar()    void scan(???#whatcanfly  **FlyObject& flyer**);
  • 重载一个scan?
  • class FlyObject() virtual void fly()=0;
  • 串联了由于行为共性而本不相关的类型

被否定的多重继承???只有虚函数,但是没有数据成员。接口是一个抽象的小类

构造函数可能是虚函数?不可能,构造函数恒无二意性,与多态无关

析构函数是虚函数?一般是

void fun(Base* base)   …delete(p)
class Base()     virtual ~Base();    
  • 保证继承的所有子类析构于其类型
  • 降低了性能:假如有一个虚函数(已经存在虚指针),其他的能虚就虚
  • static与行为正确性无关,与虚无关

template——reuse

class Stack{
    int pool[100];
    int top;
    void push(int i);
    int pop();
}

如何建一个double的栈?万能的栈?

template <class T> class Stack{
    T pool[100];
    int top;
    void push(T i);
    T pop();
}
  • 模版类
  • T为类型参数
  • 行为上毫无区别,只是类型区别
  • 容器最适用使用模版——-STL
#include<vector>
vector<int> vi;
for(i=1;i<=10000;i++) vi.push_back(i);
  • 万能容器
  • 动态增长
  • 重载了运算符<<

list vector

  • 工程:价值导向,只有0和1。在性能问题出来之前不考虑
  • 科学:真理导向,向前曲折推进

Iterator——所有STL类的内部类

vector<int>::iterator it = vi.begin();//*it=0
while(it !=vi.end()){
    cout<<*it<<endl;
    it++;
}
  • it伪装成指针,重载了++ -- *

Factory模式

Sample sample=new Sample();

可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。
首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:

Sample sample=new Sample(参数);
  • 如果初始化是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的
  • 创建实例所需要的大量初始化工作从Sample的构造函数中分离出去
    需要将Sample抽象成一个接口.
Sample mysample=new MySample();
Sample hissample=new HisSample();

随着项目的深入,Sample可能还会"生出很多儿子出来", 你会建立一个专门生产Sample实例的工厂:

public class Factory{
    public static ISample creator(int which){
        if (which==1)
            return new SampleA();
        else if (which==2)
            return new SampleB();
    }
}

那么在你的程序中,如果要创建Sample的实列时候可以使用
Sample sampleA=Factory.creator(1);
这样,在整个就不涉及到Sample的具体的实现类,达到封装效果,也就减少错误修改的机会

graph LR
产品接口---接口实现类
接口实现类---工厂
工厂-->生产产品接口的具体实例

Observer模式

详见深入浅出设计模式——观察者模式(Observer Pattern)

问题探讨

案例1

  • 代码
#include<iostream>
#include<cstring>
using namespace std;
class Person {
	public:
		virtual void print(){
			cout<<"I am person!\n";
		}
}; 
class Student:public Person {
	public:
		virtual void print(){
			cout<<"I am student!\n";
		}
}; 
void test(Person& p){
	p.print();
}
int main(){
	Person a; Student b;
	Person *xa =&a; Student *xb = &b;
	memcpy(xa,xb,4);
	a.print();
	test(a);
	(*xa).print();
	return 0;
}
  • 运行结果
I am person!
I am student!
I am student!
  • 类图
  1. memcpy之前
    在这里插入图片描述
  2. memcpy之后
    在这里插入图片描述
  • 原因:
    • a.print();采用的是静态链接,实际上使用的是memcpy之前的print方式
    • test(a);
      (*xa).print();为指针或引用,采用动态链接,使用的是memcpy之后的print方式

案例2

  • 代码
class Base
{
public:
    virtual void function1() {};
    virtual void function2() {};
};
 
class D1: public Base
{
public:
    virtual void function1() {};
};
 
class D2: public Base
{
public:
    virtual void function2() {};
};
  • 类图
    3

虽然这个图有点疯狂,但它非常简单:

  • 编译器会添加一个隐藏指针,指向使用虚函数的最基类Base
  • D1,D2从Base继承*__vptr
  • 每个类中的* __ vptr指向该类的虚拟表。
  • 虚拟表中的条目指向允许调用该类的函数对象的最派生版本。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值