代码改变世界

侯捷C++八部曲:C++面向对象程序设计

2021-11-22 00:07  cascle  阅读(870)  评论(0编辑  收藏  举报

1. C++编程简介

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 2.头文件与类的声明

c语言的数据暴漏的太多,任何函数都能访问,不容易维护

 

 

类分两种,内部带指针的和不带指针的

 

 

 类里有指针要非常小心

 

 

 

 

 .h和.cpp是一部分,因为角色的区分,分成两部分

自己的头文件用双引号,库的用尖括号

 

 

 可以把.h拿掉,c的stdio要写成cstdio

 

 

 

 

 

 

 

 

 

3. 构造函数

 

 

 

 private:主要作用是要封装起来内部细节

访问级别可以任意交错

 创建对象的时候构造函数自动被调用。不需要返回值,因为不需要自己创建本对象。初始化列表构造函数独有。数值设定分两阶段,用初始化和赋值,先初始化再复制。初始化列表属于初始化,构造函数体是赋值

初始化列表的使用显得大气上档次

不带指针的类,多半不用写析构函数

 

 

 重载是给函数改名

构造函数一个是有默认参数,一个是没有参数,这样不行,两者冲突,因为编译器不知道选哪个函数,结果不唯一

c1和c2()这两种方式创建对象都可以

 

 4.参数传递与返回值

 

 

 

 

 

 函数在参数和body之间加const,代表函数不会改变对象内容

const对象调用非const方法会报错

不改值的函数用const修饰是个好习惯

 

 尽量不要pass by value,复制浪费时间、空间。引用像指针但是更漂亮,使用更自然。引用、指针一般更快、更省空间,但和小字节数据比还是差

按值传递是复制参数,改了参数也不会改原值,引用为了达到这种效果,要用const在变量类型前修饰,这样确保不会改变实参

 

 返回引用效率更高,返回值尽量是reference,有的情况不能返回ref

 

 友元比用函数拿效率高点,但是打破了封装

 

 

 

正规写法:

1.数据放private

2.参数尽可能以reference来传,根据要不要改实参决定要不要带const

3.返回值尽量以reference来传

4.在类本体里定义的函数,不改值的加const

5.构造函数用初始化列表初始化

返回reference:返回的是一个本来就有的东西,不会有错。local对象就不能做为reference返回

reference主要用在传递东西上

 

5. 操作符重载与临时对象

 

 编译器自动把c2地址传进来当成this,this是函数的隐藏参数

 

返回的是引用,接收端怎么接收不用在乎,用引用和value接收都可以,方便书写,只是效率有差别。传回指针就不行了,必须用指针接收,无法连着用。如果不加引用返回一个已存在对象,相应的会生成一个临时对象,构造、拷贝、析构分别来一次(造一个对象,要不用来常量引用要不用来拷贝,对左值修改影响不了原值,引用直接编译报错;NRVO可以优化不生成对象,直接走拷贝,效率一致,但NRVO可能失效),引用则是指针赋值或者拷贝一次。返回临时对象少复制一次只有一次构造,有编译器优化(RVO)

 

 

 

 重载只有一个能符合,多了编译器报错

多个参数,顺序不定,用全局的容易掌控

参数没有this pointer

 

临时对象,没有名字,声明在下一行已经结束了 

 

运算符 全局重载,靠参数个数确定函数名.只有一个参数放对象左边的,可以设计为全局函数也可以设计为类函数,运算符重载函数的结合性和默认的是一样的

 

 

 

 

 

有些类不能修改,要用全局运算符重载其运算符

不是自己写的,参数别加const

 

 

6.复习Complex类的实现过程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

7. 三大函数:拷贝构造,拷贝复制,析构

 

 

 

 

 拷贝构造与拷贝赋值区别:一个是构造初始化一个是赋值,拷贝构造的对象之前不存在,所以与赋值构造处理不同,不用清理以前的垃圾

默认的是按bit复制,没有指针、引用就够用

 

 

 

 

 s1()也会调用构造函数

 

 

 浅拷贝,内存泄漏和数据别名

 

拷贝构造函数只是复制,不想改变外面对象,所以要const参数

 

 

 

 判断是否是自己,清空已有数据,拷贝新数据。给已有对象重新赋值

 

 

 

 

 

 

 

 没有自我赋值检测,一是慢,二是产生悬垂指针

 

 

 

8,堆、栈与内存管理

 

 对象的内存,要不位于栈区要不位于堆区

 

 

 析构函数自动被调用

 

 

 

global对象构造函数早在main执行前就被调用

类中的静态变量与全局变量相同。它的构造函数将在进入 main 函数之前被调用。

  • 结论(适用于 g++、Windows 环境):
  • 全局变量 类中的静态成员 : 在输入之前调用构造函数 功能 (1) .
  • 局部静态变量 : 构造函数仅在第一次执行到达其声明时调用。
  • 局部静态变量为 POD 类型 , 那么在输入 之前也是初始化的主 功能 (1) .
    POD 类型示例:static int number = 10;

(1) :正确的状态应该是:“在调用来自同一翻译单元的任何函数之前”。但是,为了简单,如下例所示,它是主要功能。

 

 

 

 内存泄漏:没机会再delete堆上对象了,失去了控制

 

"operator new",函数名就叫这个

 

 删除两块堆上的内存

 

 上下红色cookie用于记录整块大小,方便回收

最后一位,0x41, 1用来表示已分配不可用,0用来表示可用未分配。最后一位可以借位是因为地址一定是16的倍数,所以最后一位必为零

 

 

 数组个数排在数据前面

 

这里指针对象、数组指针还是指向对象内存地址的,而不是cookie等附加数据,相应的delete函数会自己去找

 写不写中括号不影响内存删除,但是影响析构函数调用,内存泄漏的是数据里的指针指向的堆内存。如果是复数类而不是字符串,加不加中括号都行

 

9. 复习String类的实现过程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10.  扩展补充:类模板,函数模板,及其他

 

 

 静态成员数据是类只有一份,静态函数只能操作静态对象成员

 

静态成员数据要在类外面初始化,不然编不过。

这也叫静态成员的定义,定义会开内存空间。类里面的是声明而已,因为静态成员脱离于对象,是属于类的

 

 

 

 

 懒汉与饿汉模式区别

 

 

 

 

 

 

 template里typename和class相通

类模板明确指出type,函数模板是根据参数自行推导

 

 

 namespace解决同名问题

 

 

 

 

11. 组合与继承

 面向对象编程:类和类之间产生关系

 

 

 

 

 黑色菱形代表里面有东西

 

 

 

 

 编译器帮助调用部件的构造、析构。

各自管各自的构造和析构即可,编译器全做好了

如果调用默认的构造、析构不合适,就自己在初始化列表上调用组件的构造函数

 

 

 白色菱形是委托,用指针指向,有些虚

 二者寿命不一致

pImpl模式:左边是接口,真正的实现在委托那里,方便实现变动、切换,这样就具有了弹性

这种手法也叫做编译防火墙

还有就是cow技术,改的时候才真正开辟新内存进行拷贝,不然就是复制一个指针

 

 白色三角形代表继承

 

 基类析构不是virtual,可能发生只调用基类析构不调用子类析构

 

12. 虚函数与多态

继承主要作用是要搭配虚函数,内存布局倒是其次

 函数的继承不应该从内存的角度理解,函数的继承继承的是调用权

 子类可以调用父类的函数

 

形状很抽象,没什么东西就直接是形状,所以要定义有纯虚函数

虚函数:希望子类来做,父类不做或者做的默认实现比较简单

 

 

 

 关键动作延缓到子类去做,叫做template method

 本质是虚函数通过vptr来调用,看是this指针是哪个对象就调到哪个函数

 

 

 

 既有基类又有Component,先调用基类构造函数

 

13. 委托相关设计

 

 

 

 

组合模式:容器可以容纳单体及容器

C++容器里要放指针,因为必须一样大小,放对象可能基类、子类不一样大

 

 

 原型模式:应对未来之对象,要求其继承已有对象,用静态对象创建自己,构造函数里将其add进框架,框架提供虚函数,子类实现copy子类自己创造的东西

LSAT:带下划线,是静态的,所以类自己创造了自己

类图先写名字再写类型

-代表private,#代表protected

通过存储的指针调用相应类的clone方法,clone里调用protected构造函数防止调用私有的构造函数又注册一次

框架应有之意:实现框架预置的方法,框架先写好了又要让他知道自己