显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Mihooke's blog

IT之恋

 
 
 
 
 
 

[置顶] 关于mihooke这个人

2015-4-30 23:20:51 阅读123 评论0 302015/04 Apr30

本人网名Mihooke,源自航海王里的原七武海之一"鹰眼"---乔拉可尔·米霍克,甚喜之,遂取之音译英文于自用。

Mihooke是茫茫猿类中的一枚C++ developer ,目前正在成长中,努力学习各种技术!

联系我:

QQ:1733133832E-mail:sadandeduoluozhich@163.com         mihooke@hotmail.com

技术宅们,有兴趣一起交流吧!

作者  | 2015-4-30 23:20:51 | 阅读(123) |评论(0) | 阅读全文>>

使得一个函数返回固定元素的数组

2017-6-10 18:31:13 阅读8 评论0 102017/06 June10

在C++11之前,如果我们要实现一个函数返回一个数组,比如,返回一个int数组,通常我们会定义一个int指针,而这个指针一般是指向堆内存(因为函数不能返回局部对象),然后让该函数返回该int指针,类似

int *MyFunc(...)

{

int *ptr = new int[NUM];

return ptr;

}

这样做会有一些小问题:该函数的使用者必须时刻牢记释放内存。

C++11提供了一种返回数组的方法,示例如下

typedef int arr[NUM];

using arr = int[NUM];

arr *func()

{

}

其中using语句就是表示arr是包含NUM个int元素的数组,arr可以直接当做函数的返回值。

还有一种方法就是声明一个返回数组指针的函数,示例如下

int (*func(int i))[NUM];

这样的表示法也是返回一个数组的指针,该数组包含NUM个int元素。

另外,C++11还提供了一种方法:尾置返回类型,这种方式可以随意设定函数的返回类型,示例如下

auto func(int i) -> int(*)[NUM]

在函数的参数之后加个指针运算符,后面是返回类型,比如int(*)[NUM]就是包含NUM个int元素的数组指针。

作者  | 2017-6-10 18:31:13 | 阅读(8) |评论(0) | 阅读全文>>

socket发送结构体--不使用封包拆包

2017-5-20 11:07:18 阅读15 评论0 202017/05 May20

socket通讯中传输struct怎么办,第一我们想到的肯定是对struct进行封包和拆包,封包和拆包是为了防止粘包的发生,即在发送struct的前面加一个4字节的变量,该变量表示要发送struct的长度(以字节为单位计算),并且在发送端我们还要对每个struct的成员进行单独封包,并转换成网络字节序;在接收端首先接收4个字节,读取出该次通信所要接受的长度,再对每个struct的成员进行单独拆包,并转换成主机字节序,有多少个成员,那么就需要进行多少次的封包和拆包,这个过程看起来有点繁琐,其实这是很规范的写法。

这里有一种偷懒的做法,既然socket中通讯的是字节流,而char的单位也是字节,我何不把struct中的每个数据转为char进行传输呢?只需要在接收端再按照固定的格式把接收到的数据进行内存拷贝至struct中。示例如下:

发送端:

typedef struct Send_data

{

char c;

short s;

int n;

double d;

char arr[10];

}SENDDATA;

SENDDATA oData;

oData.c = '1';

oData.s = 10;

oData.n = 100;

oData.d = 1.2f;

strcpy(oData.arr, "mihooke");

send(sockWorker, (char*)&oData, sizeof(oData), 0);

作者  | 2017-5-20 11:07:18 | 阅读(15) |评论(0) | 阅读全文>>

select实现多个client连接

2017-5-13 19:50:18 阅读13 评论0 132017/05 May13

利用select实现server和多个client实现通信。Master作为server,Worker和PLCWorker作为client。select函数在listen和accept中间使用

Master.cpp

#include <iostream>

#include <WinSock2.h>

#include <tchar.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

int main()

{

WORD wVersionRequsted;

WSADATA wsaData;

wVersionRequsted = MAKEWORD(2, 2);

int nErr = WSAStartup(wVersionRequsted, &wsaData);

if (nErr != 0)

{

WSACleanup();

return 1;

}

if (LOBYTE(wsaData.wVersion) != 2 && HIBYTE(wsaData.wVersion) != 2)

{

WSACleanup();

return 2;

作者  | 2017-5-13 19:50:18 | 阅读(13) |评论(0) | 阅读全文>>

创建子进程来建立多个client连接

2017-5-13 19:40:58 阅读13 评论0 132017/05 May13

Master要和2个worker:worker1和Woeker2通信,当Master和worker1建立通信之后,可以创建一个子进程SubMaster,SunMaster和Worker2进行通信,从而达到server和多个client进行通信的目的。

Master.cpp

#include <iostream>

#include <WinSock2.h>

#include <wchar.h>

#include <tchar.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

#define MYNAMEDPIPE _T("\\\\.\\pipe\\MIHOOKE")

int main()

{

WORD wVersionRequired;

WSADATA wsaData;

wVersionRequired = MAKEWORD(2, 2);

int nErr = WSAStartup(wVersionRequired, &wsaData);

if (nErr != 0)

{

cout<<"WSAStartup ret error"<<endl;

WSACleanup();

作者  | 2017-5-13 19:40:58 | 阅读(13) |评论(0) | 阅读全文>>

回调和观察者模式

2017-4-11 21:13:19 阅读28 评论0 112017/04 Apr11

在C和C++中,回调是一个模块中的函数指针,可以调用另一个模块中的函数,并且后者不存在对前者的包含或链接依赖。回调的这种特性,使得低层代码能够执行与其不能有依赖关系的高层代码。比如,上位机程序需要调用算法DLL的函数进行处理工作,算法提供了相应头文件和DLL,我们在上位机程序中就可以使用函数指针来调用算法接口函数,而不用让整个工程包含算法DLL编译库,从而降低两者之间的耦合。

有时候,需要通过回调函数来传递参数,可以用一个“闭包”来解决,下面是一个例子:

class CallBackClass

{

public:

typedef void(*CallBackFunc)(char *str, void *data);

void SetCallBack(CallBackFunc, void *data);

private:

CallBackFunc m_cbf;

void *m_data;

};

使用:

if (m_cbf)

{

(*m_cbf)("mihooke", m_data);

}

不过C++中这种封装使用有一个问题,那就是非static成员函数自带this指针,因为this指针也需要传递,这时候当作回调函数会比较复杂,可以为每个成员函数创建一个static方法,并且使用额外的回调参数来传递this指针。

作者  | 2017-4-11 21:13:19 | 阅读(28) |评论(0) | 阅读全文>>

Pimpl

2017-4-10 21:28:19 阅读23 评论0 102017/04 Apr10

Pimpl(private implementation),它的意思是私有实现,尽量保证实现不透明。比如我们正在封装API,类似于下面情况:

Header1.h

#include "Header.h"

class Header1

{

public:

Header1();

~Header1();

int GetHeaderValue(Header *ph) const;

private:

int m_nData;

Header m_header;

};

Header1.cpp

Header1::Header1() {}

Header1::~Header1() {}

int GetHeaderValue(Header *ph) const {}

由于Header1.h是对客户开放的头文件,所以我们的细节实现类Header也开放给了客户,因为Header1.h中include了Header.h,否则Header无法在Header1类中使用。一般来说,不在头文件中包含头文件是一个比较好的习惯。我们可以用Pimpl方法来避免这种情况,如下:

Header1.h

class Header;

作者  | 2017-4-10 21:28:19 | 阅读(23) |评论(0) | 阅读全文>>

红黑树介绍

2017-3-1 18:35:39 阅读36 评论0 12017/03 Mar1

红黑树,一种二叉查找树。

二叉查找树,有如下性质:

若任意节点的左子树不为空,则左子树的值均小于它的根节点的值若任意节点的右子树不为空,则右子树的值均大于它的根节点的值任意节点的左、右子树均为二叉查找树没有键值相等的节点空树也是二叉查找树

那么,红黑树就是在二叉查找树的每个结点上增加一个存储位来表示节点的颜色,只能是红色或黑色,通过对任何一个节点着色方式的限制,红黑树确保没有一条路径比其它路径长出两倍。它有如下性质:

每个节点要么是红的,要么是黑的根节点是黑的每个叶子节点也是黑的如果一个节点是红色的,那么它的左右儿子节点是黑的对于任何一个节点,其到叶子节点的每条路径都包含相同数目的黑节点。

这样的特性保证了红黑树相对平衡,从而使得查找、删除、增加操作时间复杂度最坏为O(logn)。

当对红黑树进行插入删除操作时,会破坏树的结构,从而红黑性质也会破坏,通过对树的旋转和重新着色来调整。

STL中set和map容器都是采用红黑树的结构来维护的,具有自动调整树结构的特性。

这就是为什么set和map的插入删除操作效率比其他序列容器要高的原因,插入和删除的时候,只要变动对应指针指向即可,,并不需要做内存拷贝和内存移动;既然内存没有变化,那么相应的iterator也没有变化了,还是保存先前的值,所以在插入和删除操作之后,先前保存的iterator并不会失效

作者  | 2017-3-1 18:35:39 | 阅读(36) |评论(0) | 阅读全文>>

C++的反射

2016-12-29 21:07:35 阅读46 评论0 292016/12 Dec29

C++的反射:可以通过类的名字得到对应类型的对象

C++语言本身是不支持反射的,但实际应用中总是会有将对象序列化的需求,总不可能C++不支持,我们就不用C++了,既然发明C++的大师们没有考虑这个,那我们只有自己动手了,毛主席说过“自己动手,丰衣足食”!

天生限制

C++语言本身不支持反射机制,但C++对象总是要序列化的,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。

但是问题出现了,C++语言本身不支持反射,也就是说不能通过如下方式生成一个对象:

ClassXXX object = new “ClassXXX”;

工厂方法

当然,这样的方法不行,那我们只有另辟蹊径。最简单的就是工厂方法了:

ClassXXX* object = FactoryCreate(“ClassXXX”);

至于FactoryCreate的设计就很简单了,if的集合就可以了:

if(name = “ClassXXX”)

return new ClassXXX;

作者  | 2016-12-29 21:07:35 | 阅读(46) |评论(0) | 阅读全文>>

class Base1

{

int m_i;

public:

Base1(int i) {m_i = i;}

virtual int sum() {return m_i;}//如果sum函数是非virtual函数,那么callfun(d)依旧调用的是Base1::sum,此时发生了类型向上转换;如果是virtual函数,那么根据多态原理,会调用派生类的sum函数

};

class Derived1 : public Base1

{

int m_j;

public:

Derived1(int i, int j) :Base1(i), m_j(j) {}

virtual int sum() {return Base1::sum() + m_j;}

};

void callfun(Base1 &b)//如果函数参数是非引用,会发生切片,那么callfun(d)依旧会调用Base1::sum

{

cout<<b.sum()<<endl;

作者  | 2016-12-29 21:05:09 | 阅读(38) |评论(0) | 阅读全文>>

查看所有日志>>

 
 
 
 
 
 我要留言
 
 
 
留言列表加载中...
 
 
 
 
 

发现好博客

 
 
列表加载中...
 
 
 
 
 
 
 
博友列表加载中...
 
 
 
 
 

天气

 
 
模块内容加载中...
 
 
 
 
 
 
 
列表加载中...
 
 
 
 
 
 
 
心情随笔列表加载中...
 
 
 
 
 

标签

 
 
数据加载中...
 
 
 
 
 
 
 

北京市 海淀区 摩羯座

 发消息  写留言

 
QQ1733133832
E-Mail sadandeduoluozhich@163.com
博客等级加载中...
今日访问加载中...
总访问量加载中...
最后登录加载中...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017

注册 登录  
 加关注