培养正规的、大气的编程习惯
头文件声明
如果没有定义就定义一个complex。如果定义过了就不再进入头文件的定义,防止多次引用一个头文件这正是我们想要的。
模版T
通过模版T来指定类型
inline函数
类里面定义的函数就是inline函数
不在本体中定义的可以加一个inline
访问级别
所有的数据都应该放在private,有些函数只打算函数内使用设为private
构造函数
创建一个对象,创建一个东西
函数名称和类名相同
可以有默认值
构造函数其实就是代表创建这种东西(就是这个类)所以没有必要有返回类型
利用初始列初始化,
re =r
im = i
为什么要在这里初始化呢?
与在函数里面初始化的区别是:
第一阶段是初始化初值列
第二阶段是大括号里面的赋值
如果在第二阶段大括号里面赋值放弃了第一阶段的初始化,效率变差
不带指针的类多半不需要析构函数
多种构造函数(重载)
构造函数放在private里面
private不被外界调用,下面的调用会报错
那放在private有什么用呢?
设计模式中有一个Singleton,就把构造函数放在private里面
Singleton意思为为单体
里面自己准备了自己
只有通过这个函数才能获取自己
告诉我们确实有这种写法把构造函数放在private里面。
常量成员函数
对class里面有不改变数据和改变数据的,
只是拿出来,
前面加上const,表示我这个对象是不可以改的
我们在底下应用的时候不加const不会报错,但是我们要写出大气的代码,要考虑周全
如果没有const就会报错
参数传递by value 或by 引用
by value就是整包都传过去,value多大就放过去 (栈)
尽量传引用,过去C中都是指针(4字节很快)
**C++我有一个指针但更漂亮–引用 **
尽量所有的参数传递都传引用
如果是字符呢? 1个字节 但是大范围都是引用
如果不希望对方改 加cosnt。
返回值by 引用
尽量返回值为引用,后面再谈。
友元
这个函数可以直接拿private里面的内容,因为是friend。
朋友直接拿数据会快一点相比函数
朋友可以打破封装
同一个class中的各个对象互为友元
看似打破了封装,其实是成立的。
小结:
-
数据一定放在private里面
-
参数尽可能以reference来传,要不要const视情况而定
-
返回值尽量以reference来传。首先考虑我用reference可不可以
-
在类的本体里的函数应该加const的就应该加,否则设计者想用的const的时候你没加会报错。
-
构造函数有一个特殊的语法叫innegerlazationlist:尽量去用它。
操作符重载(成员函数) this
所有的成员函数里面都有一个隐藏的参数,这里是this其实虽然他没写但是他是隐藏的,
我们写成下面的this就错了,
你不能在参数列里把this写出来,但是你可以在里面用
左边的c2对应是this最终加到c2上。
谁调用我,谁就是那个this,,this是一个指针 。
传递者无需知道接受者是以引用接收,就像c1作为传递者 ,const compex& r作为接收者
操作符重载(非成员函数) this,创建临时变量
之前用到complex&是加到左边,那个东西是存在的
而现在是加到一个不存在的东西(临时对象),所以不可以是引用
蓝色部分是return by value
因为我们return 的是一个临时变量
typename():
创建临时对象
complex(); // 0,0
C++中没有作用于右边操作的语法
左边是cout
操作符有成员函数的写法,也有非成员函数的写法
对着cout这种特殊的只有用全局的写法,因为对于以前现有的东西像等<<这种现有的符号不要想着写为成员函数的写法
这里不可以加const
如果加const,传进来的os不可被改变
这里也看不出来我改他啊,其实往os里面丢的时候其实在修改
每一次输出都在改变os的状态。
7.三大函数:构造拷贝、拷贝复制、析构
带指针的类
如果是带指针的就不能用编译器的那一套拷贝,会让指针指向同一个地方。
Big Three,三个特殊函数
下面的是我们前面介绍的构造函数:
(1)拷贝构造
接受自己的东西:拷贝构造
(2)拷贝赋值
赋值操作符重载,右边赋值还是自己这个东西:拷贝赋值:
(3)析构函数
构造函数和析构函数
带有指针的类必须要有拷贝构造和拷贝赋值
下面就是浅拷贝的动作。
拷贝构造应该创建足够的空间存放蓝本
这两个等同,都是创建一个新东西去接收蓝本。
如果没有写这个
而是把指针给过去,那就是浅拷贝。会造成内存泄露
alias:别名。
拷贝赋值函数:
本来就有的两个东西,需要把原来的东西清空,分别配一个和原来一样大的空间,把数据拷贝过来
把s1作用到s2身上,this就是指向s2根据上面的123流程如下:
(1)先清空s2自己的m_data
(2)然后重新分配一个空间给s2
(3) 拷贝过来
另外:功力深厚才能写出来这句话,这句话一定要有:
如果是相同的元素赋值进来,没有这句话的话:
先把自己删掉,后面再进行赋值自己已经不存在了。
8.堆、栈与内存管理
先调用析构函数,后释放内存。
动态分分配所得的内存块:
红色为cookie 为 4 2
灰色为84+4
再加上conplex 8
=52
vc每一个区块都是16
pad填补后为64
64十六进制为0x40这个地方给你了进1所以是0x41,如果把这个地方还给操作系统就变成0x40
Debug模式下会有32+4(上面的灰色空间、下面的黄色空间)
因为是3个complex会有4个字节记录为3;
80的16进制50,给出这块空间+1 所以是51
array delete
如果delect数组的时候不写[] 系统只会删除第一个,他不知道还有2和3
会造成内存泄漏。
9.复习String类的实现过程
定一个指针类型的char* 4个字节。 后面通过动态分配来提供空间
类里面带有指针:关注big three 3个特殊的函数
拷贝构造;拷贝赋值
传进来的数据不改变 形参加上const,拷贝构造需要一个蓝本,蓝本就是他自己 ,加上引用。
传出去的数据不改变 后面加上const
构造函数:
把很复杂的函数也写成inline,没关系,如果不能inline编译器自然不会去做
拷贝赋值:
从来源端到目的端
目的端是已经存在的东西,所以目的端需要把自己清掉
再分配一块够大的空间,给他重新赋值
其实我们函数名设为void 不return某种情况下OK
但是在某种连串赋值的情况下就不可以。
传出去的是一个东西 return *this,不管你接收是by value还是by reference方式接收
传出去的人
不必知道接收端是什么形式来接收,
如果你要指针或引用就看你函数的返回类型怎么写了
10 类模版、函数模版及其他
static 静态数据或函数
this可写可不写,不写编译器会帮你加上
static单独会有一个区域存放
并且静态函数没有this point
可见不能像一般的去处理
里面是声明static double m_rate;
外面黄色部分是:静态变量的定义,给不给数据都可以。
- 对于static: 1.类内声明,类外初始化
- 2.静态成员函数只能访问静态成员变量
- 3.通过class名调用或者通过对象来调用
調用static 函數的方式有二:
(1) 通過object 調用
a.set_rate(7.0);
(2) 通過class name 調用
Account::set_rate(5.0);
单例模式(设计模式)
前面说到的单例模式
我们写的class只希望一个对象
在private里面放一个 自己的静态A ,在没有创建A的对象的时候,他已经在一个了,我不想让外界创建A,把他的构造函数放在private里面
没有任何人能创建它了,他只有一个自己A
外界如何取得它的,通过静态函数:这个函数就是把刚刚他的自己return回去,所以getInstance是外界的唯一接口
通过得到的唯一的A去 获得类里面的其他函数
但是这个写法不是最完美的
把静态的自己放在函数里,当有用户调用到他(getInstance)他才会创建
离开这个函数后,A还在
如果没有任何人使用这个单例,这个单例就不存在
如果有人用了这个单例也就永远只有这一份。
cout:
函数模版:
namespace
11 组合与继承,设计模式与容器
复合
Adapter(设计模式)
A拥有B,A的所有功能都让B来做
B:deque 两端都可以进出
A:queue 一端进出
相当于我有一个很强大的东西,稍微改装一下
复合的空间大小:
A包含B:(红色编辑器帮我们加上的)
构造先B后A(B在外面先执行)
析构先A后B(B在括号里面后执行)
委托composition by reference
A里面有B,但是B有点虚 是一个 指针
需要右边的B才会创建而复合里面的B是一创建就有的
右边怎么变化都不影响左边,不影响字符串
就像编译防火墙 左边永远不会变,变的是右边
继承
子类里面会有父类的part
三种继承方式 public private protected
12.虚函数和多态
父类里面的函数,上面这些子类都可以调用
子类要不要重新定义:
(1)非虚函数:你不希望子类重新定义
虚函数重新定义才能用override(复写).
(2)虚函数:你希望子类重新定义它,并且他已经有默认定义
对于上面的error他有默认的提醒错误的消息,如果你想对于子类不想用默认的我允许你重新定义它,如果有哪一种形状出错,自然调用那一种error
(3)纯虚函数:你希望子类一定要重新定义它,你对他没有默认定义
Tempate Method(设计模式)
父类CDocument的serialize是抽象的
需要子类以后把他实体化
CMyDoc继承了CDocument类,它里面有一个seralize我暂时不想处理,希望让子类去处理它。
当子类调用OnFileOpen的时候会回到父类去调用父类的函数OnFileOpen,因为父类里面有seralize
到这里时,会运行子类里面写的serialize也就是后来让子类去完成的函数。
继承加复合下的构造和析构
(1)
#include<iostream>
#include<string>
#include<list>
using namespace std;
// Derived继承于Base,Derived里面又有一个Component
class Base{
public:
Base()
{
cout << "Base的构造函数" << endl;
}
~Base()
{
cout << "Base的析构函数" << endl;
}
void printBa()
{
cout << "Base" <<endl;
}
};
class Component{
public:
Component()
{
cout << "Component的构造函数" << endl;
}
~Component()
{
cout << "Component的析构函数" << endl;
}
void print()
{
cout << "Component" << endl;
}
};
class Derived : public Base{
public:
Derived()
{
cout << "Derived的构造函数" << endl;
}
~Derived()
{
cout << "Derived的析构函数" << endl;
}
void printDe()
{
abc.print();
}
private:
Component abc;
};
void test01()
{
Derived c;
c.printDe();
}
int main() {
test01();
return 0;
}
(2)
这种情况下构造函数和析构就很清晰了
当然是最里面的Conponent构造先调用,析构最后调用。
委托+继承(Observer设计模式)
ppt中一个大窗口看同一份东西: