类和动态内存分配
动态内存和类
C++在分配内存时让程序在运行时决定内存分配,而不是在编译时决定。这样,可根据程序的需要,而不是根据一系列严格的存储类型规则来使用内存。
C++使用new和delete运算符来动态控制内存,在类中使用这些运算符将导致许多新的编程问题。在这种情况下,析构函数将是必不可少的。有时候,还必须重载赋值运算符,以保证程序正常运行
静态类成员有一个特点:无论创建了多少对象,程序都只创建一个静态类变量副本。也就是
说,类的所有对象共享同一个静态成员
不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分
静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符
来指出静态成员所属的类。但如果静态成员是整型或枚举型const,则可以在类声明中初始化
new被分解为三个动作:
调用函数operator new 函数,其内部用malloc分配内存
把第一个动作得到的指针进行转型使之与Complex形态相符
通过二得到的指针调用构造函数给变量赋初值
delete被转换为两个动作
首先调用析构函数
cookie:记录整快内存块的大小
array new 一定要搭配array delete使用
在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用new[](包括中括号)来分配内存,则应使用delete[](包括中括号)来释放内存。
特殊成员函数
C++自动提供了下面这些成员函数:
- 默认构造函数,如果没有定义构造函数;
- 默认析构函数,如果没有定义;
- 复制构造函数,如果没有定义;
- 赋值运算符,如果没有定义;
- 地址运算符,如果没有定义。
如果没有提供任何构造函数,C++将创建默认构造函数编译器将提供一个不接受任何参数,也不执行任何操作的构造函数(默认的默认构造函数),这是因为创建对象时总是会调用构造函数如果定义了构造函数,C++将不会定义默认构造函数
//设计接口
class String
{
public:
String(const char* cstr=0);
//构造函数,传入的对象为初值,不会更改传入的数据
String(const String& str);
//拷贝构造函数,拷贝的蓝本为自己,不会更改传入的数据
String& operator=(const String& str);
//拷贝赋值 来源端拷贝到目的端
~String();
//析构函数
char* get_c_str() const {return m_data;}
//返回指针
//构造函数
inline
String::String(const char* cstr=0)
{
if(cstr){
m_data=new char[strlen(cstr)+1];
strcpy(m_data,cstr);
}
else{
m_data=new char[1];
*m_data='\0';
}
}
//析构函数
String::~String()
{
delete[] m_data;
}
//拷贝构造函数
inline
String::String(const String& str)
{
m_data=new char[strlen(str.m_data)+1];
strcpy(m_data,str.m_data);
}
//拷贝赋值函数
inline
String& String::operator=(const String& str)
{
if(this==&str)
return *this;
delete[] m_data;
m_data=new char[strlen(str.m_data)+1];
strcpy(m_data,str.m_data);
return *this;
}
静态函数只能处理静态的数据
静态数据要在class类之外写定义
调用static函数的方式有二
1.通过object调用(和普通变量相同)
2.通过class name 调用
运用静态实现单例
class A{
public:
static A& getinstance(return a;);//外界只能通过静态函数得到唯一的a
set up(){....}
private:
//将A的构造函数放在private中意味着没有任何人可以创建A类的对象
//因此实现了单例中只能有一个该类对象
A();
A(const A& rhs);
static A a;
...
};
如果外界没有用到a,但单例中已经生成将会浪费内存
进阶版单例
class A{
public :
static A& getInstance();
set up(){.....}
private:
A();
A(const A& rhs);
...
};
A& A::getInstance()
{//只有调用函数时静态变量才会生成,且生成后变量不会消失
static A a;
return a;
}
cout做了多种类型的重载,因此才能将各种各样的数据类型输出
转换函数,例如将分数转换为小数
特殊的构造函数
将对象转换为像指针的东西
将对象转换为像函数的东西
模板的特化
标准库
类和类之间的关系
继承(Inheritance)
举例
-List-node为子类
三种继承方式 public,private,protect
父类的数据是被完整的继承下来的
继承关系下的构造与析构
构造由内而外(先父后子)
析构由外而内(先子后父)
父类的析构函数必須是 虚函数,否則會出現undefined behavior
Composition复合
举例
Adapter:改造,适配,配接
queue为容器,它容纳拥有了箭头所指的东西
Composition (複合) 关系下的构造和析构
构造由内而外
析构由外而内
继承+复合
委托+继承
观察者模式
让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
右边的观察类是一个可被重写的父类,监视左边数据类的变化,左边的数据类通过委托来调用观察类
因为要生成不止一个观察类,所以观察类需要被重写
数据类需要有注册和注销的功能,将所有观察者添加到容器中,还要有通知观察者更新数据的方法
Composite
在计算机的文件系统中,有“文件夹”的概念。文件夹里面既可以放入文件,也可以放入其他文件夹(子文件夹)。以此类推,文件夹是形成了一种容器结构、递归结构。
虽然文件夹与文件是不同类型的对象,但是它们都“可以被放入到文件夹中”。文件夹和文件有时也被称为“目录条目”。在目录条目中,文件夹和文件被当作是同一种对象看待(即一致性)。
有时,与将文件夹和文件都作为目录条目看待一样,将容器和内容作为同一种东西看待,可以帮助我们方便地处理问题。
能够使容器与内容具有一致性,创造出递归结构的模式就是 Composite 模式。Composite 含义有 “混合物”、“复合物” 的意思。
Composite和Primitive都是Component的子类,具有添加方法的虚函数,方便让子类进行更改
Composite(复合物)存储并添加Component的指针(委托),这样就能存储Primitive和Composite
Primitive(基础物)
Prototype
原型模式是一种创建型设计模式,允许通过复制现有对象来创建新对象,而无需经过详细的构建过程。
树状继承体系,创建现在没有而未来会出现的子类
子类中有一个静态的对象,类型是子类自己,此为原型
原型中有两个构造函数,一个私有一个友元,原型可以调用私有和友元的构造函数
私有的构造函数会调用addPrototype(继承自父类的函数,将得到的对象添加进父类的数组),将该原型挂到父类上去
在调用父类中FindAndClone这个函数时如果生成的对象还是调用私有构造函数会造成addPrototype重复的添加了相同的对象,因此要调用友元的构造函数(该构造函数也可以为私有,主要是为了与另外的构造函数做区分),参数的作用主要是为了做区分
子类中有克隆函数,框架中被添加到数组中的的原型就可以调用clone做一个副本
父类中有空间,在addPrototype将得到的原型添加进的数组之后,父类就可以成功调用原型
父类的clone是一个纯虚函数,要求子类必须写出这个函数
Question:为什么不将clon设置成一个静态函数?这样不经过原型也可以直接由父类调用
答:静态函数需要类名才能调用,而原型方法解决的问题就是在目前没有子类的情况下框架要使用的方法
委托Delegation(Composition by reference)
用指针指向类后,在任何想要的时间点都可以调用这个类
与composition的区别
composition与被容纳的东西生命周期是一起的
而delegation与被指向的类不同步,想什么时候调用什么时候调用
例子
Handle/Body(pimple)
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们都可以独立变化。这种模式有时也被称作柄体(Handle and Body)模式或接口隔离模式。它的主要目的是将抽象层与实现层解耦,使得两者可以独立扩展而互不影响。
左边只是接口,实际执行方法都在右边被指向的类中
CopyOnRight:如果要a改变不影响b和c,可以单独给a复制一份,b和c继续共享原来的内容
虚函数和多态
成员函数
非虚函数non-virtual:不希望override
虚函数virtual:希望重新定义他
纯虚函数:pure virtual 希望一定要重新定义,纯虚函数是没有定义的
Template Method(模板方法):在应用程序的框架中会大量使用这种手法,让无法决定的函数成为一个虚函数,交给子类去定义
模板方法的核心定义一个算法的骨架,而将一些细节方法延迟到之类中实现。其中,算法骨架是不变的部分,细节方法是变化的部分。设计模式的核心思想就是将变化部分和不变部分有效隔离、耦合出来。
待解决struct和class的区别