C++学习笔记之编程思想
编程思想
单例(Singleton)模式
实现思路:
- Singleton拥有一个私有构造函数,确保用户无法通过new直接实例它;
- 包含一个静态私有成员变量instance与静态公有方法Instance();
观察者(Observer)模式
在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应,对象常是一对多关系;
观察者抽象类:
#pragma once
# ifndef OBSEVER_H_1
# define OBSEVER_H_1
class Observer
{
public:
Observer() { ; }
virtual ~Observer() { ; }
// 当被观察对象发生变化时,通知被观察者调用这个方法
virtual void Update(void* pArg) = 0;
};
# endif
被观察者抽象类定义:
#pragma once
#include <string>
#include <list>
using namespace std;
class Observerable
{
public:
Observerable();
virtual ~Observerable();
// 注册观察者
void Attach(Observer* pOb);
// 反注册观察者
void Detach(Observer* pOb);
int GetObseverCount() const
{
return _Obs.size();
}
void DetachAll()
{
_Obs.clear();
}
virtual void GetSomeNews(string str)
{
SetChange(str);
}
protected:
void SetChange(string news); // 有变化,需要通知
private:
void Notify(void* pArg);
private:
bool _bChange;
list<Observer*> _Obs;
};
被观察者抽象类实现:
#include "stdafx.h"
#include "Observerable.h"
#include "Observer.h"
Observerable::Observerable():_bChange(false) { }
Observerable::~Observerable(){ }
// 注册观察者
void Observerable::Attach(Observer* pOb)
{
if (pOb == NULL) { return; }
// 看看当前列表中是否有这个观察者
auto it = _Obs.begin();
for (; it != _Obs.end(); it++)
{
if (*it == pOb) { return; }
}
_Obs.push_back(pOb);
}
// 反注册观察者
void Observerable::Detach(Observer* pOb)
{
if ((pOb == NULL) || (_Obs.empty() == true)) { return; }
_Obs.remove(pOb);
}
void Observerable::SetChange(string news)
{
_bChange = true;
Notify( ( (void*)news.c_str() ));
}
void Observerable::Notify(void* pArg)
{
if (_bChange == false) { return; }
// 看看当前列表中是否有这个观察者
auto it = _Obs.begin();
for (; it != _Obs.end(); it++)
{
(*it)->Update(pArg);
}
_bChange = false;
}
应用观察者模式:
#include "stdafx.h"
class News : public Observerable
{
public:
virtual void GetSomeNews(string str)
{
SetChange("News: " + str);
}
};
class User1:public Observer
{
public:
virtual void Update(void* pArg)
{
cout << "User1 Got News: " << reinterpret_cast<char*>(pArg) <<endl;
}
};
class User2 :public Observer
{
public:
virtual void Update(void* pArg)
{
cout << "User2 Got News: " << reinterpret_cast<char*>(pArg) <<endl;
}
};
int main()
{
User1 u1;
User2 u2;
News n1;
n1.GetSomeNews("T0");
cout << n1.GetObseverCount() << endl; // 0
n1.Attach(&u1);
n1.Attach(&u2);
n1.GetSomeNews("T1");
cout << n1.GetObseverCount() << endl; // 2
n1.Detach(&u2);
n1.GetSomeNews("T2");
cout << n1.GetObseverCount() << endl; // 1
n1.DetachAll();
n1.GetSomeNews("T3");
cout << n1.GetObseverCount() << endl; // 0
return 0;
}
void*、NULL和nullptr
- 在C语言中:
#define NULL ((void*)0)
- 在C++语言中:
#ifndef NULL
#ifdef cplusplus
#define NULL0
#else
#define NULL ((void*)0)
#endif#endif
- 在C++11中,nullptr用来替代(void*)0,NUL则只表示0;
#include <iostream>
using namespace std;
void func(void* i) { cout << "func(void* i)" << endl; }
void func(int i) { cout << "func(int i)" << endl; }
int main()
{
int* pi = NULL;
int* pi2 = nullptr;
char* pc = NULL;
char* pc2 = nullptr;
func(NULL); // func(int i)
func(nullptr); // func(void* i)
func(pi); // func(void* i)
func(pi2); // func(void* i)
func(pc); // func(void* i)
func(pc2); // func(void* i)
return 0;
}
C的类型转换
- 隐式类型转换
doublef=1.0/2;
- 显式类型转换:(类型说明符)(表达式)
double f=double(1)/double(2);
C类型转换的问题:
- 任意类型之间都可以转换,编译器无法判断其正确性;
- 难于定位:在源码中无法快速定位;
C++的类型转换
- const cast:用于转换指针或引用,去掉类型的const属性(const变量即使内存被修改,读取的值不变,参考 const变量通过指针修改 详解 )。
const int a = 10;
//int* pA = &a; //类型不一致错误
int* pA = const_cast<int*>(&a);
*pA = 100;
cout << a; //10,编译器只对const变量的值只读取一次
- reinterpret_cast:重新解释类型(很危险),既不检查指向的内容,也不检查指针类型本身;但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误。
char* a = "a";
void* b = a;
char* c = reinterpret_cast<char*>(b);
cout << c; //a
- static_cast:用于基本类型转换,有继承关系类对象和类指针之间转换,由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销。
int i = 6;
double d = static_cast<double>(i); //基本类型转换 int -> double
double d2 = 5.6;
int i2 = static_cast<int>(d2); //基本类型转换 double -> int
cout << d <<endl; //6
cout << i2 << endl; //5
- dynamic_cast:只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转化;向下转化时,如果是非送的对于指针返回NULI。
class Base
{
public:
Base() : _i(0) { ; }
virtual void T() { cout << "Base:T" << _i << endl; }
private:
int _i;
};
class Derived : public Base
{
public:
Derived() :_j(1) { ; }
virtual void T() { cout << "Derived:T" << _j << endl; }
private:
int _j;
};
int main()
{
Base cb;
Derived cd;
Base* pcb;
Derived* pcd;
// 子类--》 父类
pcb = static_cast<Base*>(&cd);
if (pcb == NULL) { cout << "unsafe dynamic_cast from Derived to Base" << endl; }
pcb = dynamic_cast<Base*>(&cd);
if (pcb == NULL) { cout << "unsafe dynamic_cast from Derived to Base" << endl; }
// 父类--》 子类
pcd = static_cast<Derived*>(&cb);
if (pcd == NULL) { cout << "unsafe dynamic_cast from Derived to Base" << endl; }
pcd = dynamic_cast<Derived*>(&cb); //此处转换失败
if (pcd== NULL) { cout << "unsafe dynamic_cast from Derived to Base" << endl; }
return 0;
}
适配器(Adapter)模式
适配器模式的定义参考 设计模式 | 适配器模式及典型应用 :
- 适配器将类接口转换为客户端期望的另一个接口;
- 使用适配器可防止类由于接口不兼容而一起工作;
- 适配器模式的动机是,如果可以更改接口,则可以重用现有软件;
适配者类(被适配的角色,已存在的接口):
class LegacyRectangle
{
public:
LegacyRectangle(double x1, double y1, double x2, double y2)
{
_x1 = x1;
_y1 = y1;
_x2 = x2;
_y2 = y2;
}
void LegacyDraw()
{
cout << "LegacyRectangle:: LegacyDraw()" << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
}
private:
double _x1;
double _y1;
double _x2;
double _y2;
};
目标抽象类(客户所需接口):
class Rectangle
{
public:
virtual void Draw(string str) = 0;
};
第一种适配的方式——使用多重继承:
class RectangleAdapter: public Rectangle, public LegacyRectangle
{
public:
RectangleAdapter(double x, double y, double w, double h) :
LegacyRectangle(x, y, x + w, y + h)
{
cout << "RectangleAdapter(int x, int y, int w, int h)" << endl;
}
virtual void Draw(string str)
{
cout << "RectangleAdapter::Draw()" << endl;
LegacyDraw();
}
};
第二种适配的方式——组合方式的Adapter:
class RectangleAdapter2 :public Rectangle
{
public:
RectangleAdapter2(double x, double y, double w, double h) :
_lRect(x, y, x + w, y + h)
{
cout << "RectangleAdapter2(int x, int y, int w, int h)" << endl;
}
virtual void Draw(string str)
{
cout << "RectangleAdapter2::Draw()" << endl;
_lRect.LegacyDraw();
}
private:
LegacyRectangle _lRect;
};
使用适配器类:
int main()
{
double x = 20.0, y = 50.0, w = 300.0, h = 200.0;
RectangleAdapter ra(x, y, w, h);
Rectangle* pR = &ra;
pR->Draw("Testing Adapter");
cout << endl;
RectangleAdapter2 ra2(x, y, w, h);
Rectangle* pR2 = &ra2;
pR2->Draw("Testing2 Adapter");
return 0;
}
结果:
RectangleAdapter(int x, int y, int w, int h)
RectangleAdapter::Draw()
LegacyRectangle:: LegacyDraw()20 50 320 250
RectangleAdapter2(int x, int y, int w, int h)
RectangleAdapter2::Draw()
LegacyRectangle:: LegacyDraw()20 50 320 250
泛型编程的思想
如果说面向对象是一种通过间接层来调用函数,以换取一种抽象,那么泛型编程则是更直接的抽象,它不会因为间接层而损失效率;
不同于面向对象的动态期多态,泛型编程是一种静态期多态,通过编译器生成最直接的代码;
泛型编程可以将算法与特定类型、结构剥离,尽可能复用代码;
模板函数
// 模板函数
template<class T>
T max(T a, T b)
{
return a > b ? a:b;
}
//特化
template<>
char* max(char* a, char* b)
{
return (strcmp(a, b) > 0 ? (a) : (b));
}
template<class T1, class T2>
int max(T1 a, T2 b)
{
return static_cast<int>(a > b ? a : b);
}
// 模板函数的测试
cout << max(1, 2) << endl;
cout << max(1.5, 3.5) << endl;
cout << max('a', 'b') << endl; //b
cout << max("hello", "world") << endl; //hello
char* s1 = "hello";
char* s2 = "world";
cout << max(s1, s2) << endl; //world
cout << max(10, 2.5) << endl; //10
模板类
// 模板类
template <class T>
class TC
{
public:
TC(T a, T b, T c);
T Min();
T Max();
private:
T _a, _b, _c;
};
template<class T>
TC<T>::TC(T a, T b, T c):_a(a), _b(b), _c(c) { ; }
template<class T>
T TC<T>::Min()
{
T minab = _a < _b ? _a : _b;
return minab < _c ? minab : _c;
}
template<class T>
T TC<T>::Max()
{
T maxab = _a < _b ? _b : _a;
return maxab < _c ? _c : maxab;
}
// 模板类的测试
TC<int> obj1(2, 4, 3);
cout << obj1.Min() << endl;
cout << obj1.Max() << endl;
TC<double> obj2(2.5, 4.4, 3.3);
cout << obj2.Min() << endl;
cout << obj2.Max() << endl;
TC<long> obj3(399950L, 455795L, 333339090L);
cout << obj3.Min() << endl;
cout << obj3.Max() << endl;
泛型递归
计算1+2+3...+100的值,使用泛型递归可以在编译期间计算出值:
// 1+2+3...+100 ==> n*(n+1)/2
template<int n>
struct Sum
{
enum Value {N = Sum<n-1>::N+n}; // Sum(n) = Sum(n-1)+n
};
template<>
struct Sum<1>
{
enum Value {N = 1}; // n=1
};
int main()
{
cout << Sum<100>::N << endl;
return 0;
}
C++学习笔记之编程思想的更多相关文章
- 学习笔记-Java编程思想
2016-01-03 Swith(整数选择因子):必须是int或char这样的整数值. Java中不包含goto语句,但是可以通过标识符实现类似的控制.
- ufldl学习笔记和编程作业:Feature Extraction Using Convolution,Pooling(卷积和汇集特征提取)
ufldl学习笔记与编程作业:Feature Extraction Using Convolution,Pooling(卷积和池化抽取特征) ufldl出了新教程,感觉比之前的好,从基础讲起.系统清晰 ...
- ufldl学习笔记和编程作业:Softmax Regression(softmax回报)
ufldl学习笔记与编程作业:Softmax Regression(softmax回归) ufldl出了新教程.感觉比之前的好,从基础讲起.系统清晰,又有编程实践. 在deep learning高质量 ...
- ufldl学习笔记与编程作业:Softmax Regression(vectorization加速)
ufldl学习笔记与编程作业:Softmax Regression(vectorization加速) ufldl出了新教程,感觉比之前的好.从基础讲起.系统清晰,又有编程实践. 在deep learn ...
- ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程)
ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程) ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在dee ...
- ufldl学习笔记与编程作业:Logistic Regression(逻辑回归)
ufldl学习笔记与编程作业:Logistic Regression(逻辑回归) ufldl出了新教程,感觉比之前的好,从基础讲起.系统清晰,又有编程实践. 在deep learning高质量群里面听 ...
- ufldl学习笔记与编程作业:Linear Regression(线性回归)
ufldl学习笔记与编程作业:Linear Regression(线性回归) ufldl出了新教程,感觉比之前的好.从基础讲起.系统清晰,又有编程实践. 在deep learning高质量群里面听一些 ...
- 异常笔记--java编程思想
开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出 ...
- 我的学习笔记_Windows_HOOK编程 2009-12-03 11:19
一.什么是HOOK? "hook"这个单词的意思是"钩子","Windows Hook"是Windows消息处理机制的一个重要扩展,程序猿能 ...
- Scala学习笔记--函数式编程
一.定义 简单说,"函数式编程"是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论. 它属于"结构化编程&qu ...
随机推荐
- 3.2 IDAPro脚本IDC常用函数
IDA Pro内置的IDC脚本语言是一种灵活的.C语言风格的脚本语言,旨在帮助逆向工程师更轻松地进行反汇编和静态分析.IDC脚本语言支持变量.表达式.循环.分支.函数等C语言中的常见语法结构,并且还提 ...
- VUE3子表格嵌套分页查询互相干扰的问题解决
VUE3在表格中嵌套子表格子表格的分页查询互相干扰的问题解决 简单嵌套 如果不需要做子表格的分页查询,那么可以直接在主表格中嵌套子表格,有两种方式:一种是主表格加载的同时加载子表格数据,另一种是点击展 ...
- centos编译安装tcpdump
环境 CentOS Linux release 7.9.2009 (Core) 准备安装包 libpcap-1.5.3.tar.gz tcpdump-4.9.2.tar.gz 下载地址:https:/ ...
- 限流设置之Nginx篇
question1:为什么用到Nginx,Nginx有什么功能? 1.反向代理(建议先看正向代理,反向代理则是同样你要与对方服务器建立连接,但是,代理服务器和目标服务器在一个LAN下,所以我们需要与代 ...
- DNS反向解析
一:创建反向解析区 1.和创建正向解析区的步骤类似,选主要区域 这里设置的IP是192.168.1,一直点确定即可 2.指针记录即反向解析记录,记录ip对应的域名,是反向解析区最常用的记录 输入ip和 ...
- NC16857 [NOI1999]生日蛋糕
题目链接 题目 题目描述 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1 ≤ i ≤ M)层蛋糕是半径为Ri, 高度为Hi ...
- Slot 的含义
Slot 解释 1)slot就是槽的意思,是一个资源单位,只有给task分配了一个slot之后,这个task才可以运行.slot分两种,map slot沪蓉reduce slot.另外,slot是一个 ...
- win32-ReadProcessMemory在x86和x64下运行
#include <iostream> #include <Windows.h> #include <winternl.h> #include <tchar. ...
- 多线程系列(八) -ReentrantLock基本用法介绍
一.简介 在之前的线程系列文章中,我们介绍到了使用synchronized关键字可以实现线程同步安全的效果,以及采用wait().notify()和notifyAll()方法,可以实现多个线程之间的通 ...
- 2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1, 同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti
2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1, 同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti ...