结构性设计模式
结构型设计模式适配器 定义:适配器是一种结构型设计模式,它能使接口不兼容的对象相互合作 运作思路 适配器实现与其中一个现有对象兼容的接口 现有对象可以使用该接口安全的调用适配器方法 适配器方法被调用后将以另一个对象兼容格式和顺序讲请求传递给该对象 组成:客户端、客户端接口、服务、适配器 类适配器 采用继承客户端接口和服务来实现 通过多继承,实现接口的转换 123456789101112131415161718192021222324252627282930313233343536373839404142434445#include <iostream>using namespace std;// Targetsclass Target{public: virtual void Request() { cout<<"Target::Request"<<endl; }};// Adapteeclass Adaptee{public: void Spec ...
创建型设计模式
创建型模式工厂方法 定义:是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型 注意:返回的产品需要共同的基类 组成:产品、具体产品、创建者、具体创建者 适用场景 当你在编写代码时,如果无法预支对象确切类别和具体关系时,可采用工厂方法 若果希望用户能扩展你的软件库或者框架的内部组件,可使用工厂方法 如果你希望复用现有对象而节省资源,而非每次都重新创建对象,可使用工厂方法 代码实例 123456789101112131415161718192021222324252627282930313233class product {};// 此处采用公有继承,否则无法实现多态的转换class apple : public product { void Print() { std::cout << "I am apple" << std::endl; }};class banana : public product ...
设计模式简介
设计模式概览面向对象编程 关键四要素 对象之间的关系 实现:类A定义的方法由接口B声明,对象A可被视为对象B(多态) 继承:类A继承类B的接口和实现,但可以对象其进行扩展 依赖:对类B的进行修改会影响到类A 关联:对象A知道对象B,类A依赖于类B 聚合:对象A知道对象B且由B构成。 组合:对象A知道对象B且由B构成。并且管理着B的生命周期 关系汇总 模式分类 创建型模式:提供创建对象的机制,增加已有代码的灵活性和可复用性 结构性模式:如何将对象和类组成较大的结构,并同时保持结构的灵活和高效 行为模式:负责对象间的高效沟通和职责委派 设计原则 面向接口进行开发,而不是面向实现 组合优于继承 SOLID原则 单一职责原则:修改一个类的原因只能有一个 开闭原则:对于扩展,类是开放的;对于修改,类则应该是封闭的 里式替换原则:当你扩展一个类时,记住你应该要能在不修改客户端代码的情况下,将子类的对象作为父类的对象进行传递 接口隔离原则:客户端不应强迫依赖于其不使用的方法 依赖倒置原则:高层次的类不应该依赖于低层次的类。两者都应该依赖于抽象接口。抽象接 ...
智能指针摘要
智能指针引入背景 悬空指针被使用:有些内存资源已经被释放,但指向它的指针并没有改变指向,并且后续还在使用 二次释放:有些内存资源已经被释放,后期又试图再释放一次(重复释放同一块内存会导致程序运行崩溃) 堆内存泄露(忘记释放):没有及时释放不再使用的内存资源造成内存泄漏,程序占用的内存资源越来越多;程序发生异常时内存泄露。 unique_ptr 内存布局(sizeof大小为8字节) 基本开销成本同裸指针一致,在未自定义删除器操作时 函数对象、lambda(注意捕获效应会导致lambda对象变大)、函数指针、函数适配器std::bind、可调用对象std::function 函数声明 template<class T, class Deleter = std::default_delete> class unique_ptr; template<class T, class Deleter = std::default_delete> class unique_ptr<T[], Deleter>; 目的是采用函数重载方式,支持对数组类对象 ...
RAII 技巧
RAII 技巧 资源获取即初始化 三个保证 构造器获取内存或资源 析构器释放内存或资源 生命周期结束后,自动调用析构器(失效即释放) 关键的意义:异常免疫,保证资源的正确释放 实例 RAII-mutex1234567891011template <class Mutex> class lock_guard {public: lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); } ~lock_guard() { mutex_.unlock(); } // 禁止复制和赋值 lock_guard(lock_guard const&) = delete; lock_guard& operator=(lock_guard const&) = delete;private: Mutex& mutex_;}; RAII-smart Pointer123456789101112131415161718192021222 ...
构造一个普适性的max函数
基本概念 指针相关 悬挂引用 指针内容指向了被释放了的内容 有趣的例子 1234567891011121314151617181920212223242526#include <iostream>#include <string>#include <windows.h>using namespace std;int* ptr = NULL;void fun() { int i = 10; ptr = &i;}int main(void) { fun(); cout << "*ptr= " << *ptr << endl; Sleep(100); cout << "一秒钟后,fun()中的i变量的存储空间被释放, ptr所指对象的值为:" << endl; cout << " *ptr = " << *ptr <&l ...
委托构造函数
构造函数 在初识C++时,构造函数的这一称谓便出现了在了我们眼前。毕竟,C++作为一个面向对象的编程语言,这是实现其特性的一个关键要素。 至于构造函数是什么,为什么关键当然是由于在实例化其对象时,所规定的一系列操作 123class Test { Test() {}} 委托构造函数 好了,浅浅的提了下基本知识。让我们迈入今天的正题好啦 首先,提问。委托构造函数是什么?他因何而生??? 让我们想想这样的一个应用场景,当我们需要利用构造函数初始化不同的函数成员时,我们想到的第一反应是什么? 123456789101112class X{public: X() : a_(0), b_(0.) { CommonInit(); } X(int a) : a_(a), b_(0.) { CommonInit(); } X(double b) : a_(0), b_(b) { CommonInit(); } X(int a, double b) : a_(a), b_(b) ...
union另类初始化
union 这是一个十分经典的地数据类型,可以最大程度上利用现有的空间,并增强的代码的灵活性 想必对编程有些的认知的小伙伴们,或多或少对此都有着一些接触 今天就让我们简单聊聊那些你在使用过程并不会注意到的点吧 union的大小,取决于最大的那个 union的指定初始化1234567union data { int a; float b;}instance;// 当然这种初始化方式同样也适用于结构体instance { .a = 0}; union支持非平凡的数据类型 这是我们今天在再聊union的一个核心目的。 自C++11之后,引入的非平凡类型,拓宽了其使用场景。12345678910111213141516171819202122232425262728293031323334353637383940414243// 与此同时,引入了一个问题便是,我们需要为撰写构造与析构函数// 下述代码,可正常,却无法运行。因为默认的构造、析构,无法很好地处理下面的非平凡类型union U { U() { ...
完美转发
万能引用 提到这个名词,在先前的描述中,我们可以知道的是,右值引用针对右值,左值引用适用于左值。但随着模版技术的引入,C++泛型能力的增强,我们如何接受一个尚未确定的引用类型呢 其实,在一开始抛砖引玉,讲述右值引用前,我便给出了此种问题的一个答案,那便是采用常量的左值引用,由于const关键字可以延长将亡值的声明周期,故可以用来接收右值引用,但不可避免的是,右值引用由于const的修饰,无法按需更改。那么,万能引用应运而生12345678void foo(int &&i) {} // i 为右值引用template <typename T>void bar(T &&t) {} // t 为万能引用int get_val() {return 5;}int &&x = get_val(); // 右值引用auto &&x = get_val(); // 万能引用 聪明如你,可能已经注意到了万能引用和右值的引用的不同之处,尽管二者均采用了&& ...
运行时多态-虚函数
虚函数 这是C++中多态的一种呈现方式,为了更好的得以了解虚函数的本身的含义与相关意义。让我们先了解下,在C++还有那些实现多态的方案。 重载。允许存在多个重名函数(C语言不支持该语法) 覆盖。是指子类重新定义父类虚函数的做法,可通过父类的指针调用实际子类的成员函数。 虚函数表 Virtual Table是一块连续的内存,每个内存单元记录一个JMP指令的地址 类别 单继承无虚函数覆盖 1234567891011121314151617class Base{public: virtual void func1(); virtual void func2(); virtual void func3();}class Derived : public Base{public: virtual void func4(); virtual void func5(); virtual void func6();}// 值得注意的点- 虚函数按照声明顺序依次放入表中- 父类的虚函数在子类的虚函数前面 单继承有虚函数覆盖 12345678910 ...