C++杂记

if

if..else和if..if
if...else结构在if判断成功后不再执行else内容,
if...if结构即使if判断成功后还会继续判断下一个if,
因此if...else效率更高。

for/while

很多源码都使用for(;;)而不是while(1),从宏观上看,两者作用完全一样,但从底层来看,for(;;)要更快。

  编译前              编译后 
while (1);         mov eax,1  
                    test eax,eax 
                    je foo+23h
                    jmp foo+18h
    编译前              编译后 
for (;;);          jmp foo+23h   

通常,for用于遍历容器,while用于等待某个操作完成。

关于if()中判断表达式的问题

if判断的实质是先进行了运算,然后将bool结果返回,
就是说,if判断了的运算执行了。

栗子1:

#include <iostream>

bool test(int& a, int& b);

int main(void)
{
   int m_a(0), m_b(0);
   if (test(m_a, m_b))
      std::cout << m_a<< std::endl << m_b<< std::endl;
   system("pause");
   return 0;
}

bool test(int& a, int& b)
{
   a++;
   b++;
   b++;
   return true;
}
1
2

栗子2:

#include <iostream>

using namespace std;
bool test(int const &a, int const &b, int &c);

int main()
{
   int a(10), b(10),c(0);
   if (!test(a, b, c))
   {
      cout << "false"<<c << endl;
   }
   else
   {
      cout << "true" <<c<< endl;
   }
   cin >> a;
   return 0;
}

bool test(int const &a, int const &b,int &c)
{
   c = a + b;
   return c;
}
true20

return

return的作用以及怎样判断返回值
return的两个作用:
返回值;
跳出函数。
https://zhidao.baidu.com/question/446678239.html

文本编辑器

千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符,你会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。建议你下载Notepad++代替记事本,不但功能强大,而且免费!记得把Notepad++的默认编码设置为UTF-8 without BOM即可.

undef

#undef取消在先前程序中对预处理器的定义。

#include <iostream.h>
#include<string.h>
#define MAX 5
#undef MAX
void main()
{
    char name[MAX]="abcd";
    cout<<"MAX = "<<MAX<<endl;
    for(int i=0;i<MAX;i++)
    cout<<name<<" "<<endl;
}

此时程序会报错,因为MAX未定义。

返回多个值

以出参方式返回多个值和以turple返回多个值.

命令行传参

在 C 语言中, 使用 main 函数的输入参数 argc 和 argv 传入命令行参数.

argc 为 int 类型, 表示传入命令行参数的个数 (argument count);
argv 为 char** 类型, 表示命令行参数向量 (argument vector), 每个命令行参数为字符串类型, 因此 参数 argv 也可以看做是字符串数组.
argv[0] 指向程序运行的全路径名.

std::function

官方说明:
Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
The stored callable object is called the target of std::function. If a std::function contains no target, it is called empty. Invoking the target of an empty std::function results in std::bad_function_call exception being thrown.
std::function satisfies the requirements of CopyConstructible and CopyAssignable.


类模板std::function是一种通用的多态函数包装器。std::function可以存储,复制和调用任何Callable 目标的实例- 函数,lambda表达式,绑定表达式或其他函数对象,以及指向成员函数和指向数据成员的指针。
所存储的可调用对象被称为目标的std::function。如果a不std::function包含目标,则将其称为空。调用目标的的空std::function的结果的std :: bad_function_call抛出异常。
std::function满足CopyConstructible和CopyAssignable的要求。

知乎解释函数指针和std::function的区别。
作者:知乎用户
链接:https://www.zhihu.com/question/314660217/answer/653075671
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
关于std::function,高赞答案说是个functor,这在代码层面是没错的,它本质上就是个functor,但设计思想层面可以认为是个类似wrapper的东西,封装对象可以是函数指针,可以是Lamda表达式,也可以是functor,甚至可以是普通函数,这个机制是C++ 11引入了的,有了它,不同callee的调用可以更加简单、一致,使用者做代码衔接的时候不必考虑兼容性和适配,可以直接拿来就用,这也是std::function的最主要作用:简化调用。为什么不用函数指针?从功能上来说,二者确实没什么区别,用functor的地方,基本换个写法都能用函数指针替代,那为什么还要有functor?,原因如下。(1)functor是用面向对象泛型思想实现的,其实不止functor,STL六大组件全部都是这种思想下的产物,而function pointer和STL整体架构思想并不一致。(2)functor本质上是对象,有对象就有数据封装,而函数指针只能用全局或局部变量。(3)functor的operator()重载,一般是比较轻量级的代码,可以被编译器自动内联化,当然,如果你喜欢写复杂代码,也没问题,但不推荐这么用。(4)函数对象可以有多态特性,更好的控制run-time behavior。

个人理解:std::function 用于格式化代码,形成抽象的统一接口。

main.cpp

#include<iostream>
using namespace std;

void gFunc()
{
    cout << "gFunc" << endl;
}
int main()
{
    std::function<void()> f = gFunc;
    f();
 
    return 0;
}
g++ main.c
./a.out
gFunc

boost::optional

boost::optional的作用是:统一化“无效值”,
表示返回值无意义最常用的做法是增加一个"哨兵"的角色,它位于解空间之外,如NULL,-1,EOF,string::npos,vector::end()等。但这些做法不够通用,而且很多时候不存在解空间之外的"哨兵".
因此可以使用 boost::optional 代表一切无效值。

#include <boost/optional.hpp>
using namespace boost;
using namespace std;
int main()
{
     optional<int> op0;                    //一个未初始化的optional对象
     optional<int> op1(boost::none);       //同上,使用none赋予未初始化值
     assert(!op0);
     assert(op0 == op1);
     assert(op1.get_value_or(253) == 253); //获取可选值

     optional<string> ops("test");         //初始化为字符串test
     string str = *ops;                    //用解引用操作符获取值
     cout <<str.c_str()<<endl;

     vector<int> v(10);
     optional<vector<int>&> opv(v);        //容纳一个容器的引用
     assert(opv);

     opv->push_back(5);                    //使用箭头操作符操纵容器
     assert(opv->size() == 11);
     opv = boost::none;
     assert(!opv);
     system("pause");
     return 0;
}

std::allocator

标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。

new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起。因为在这种情况下,我们几乎肯定知道对象应有什么值。当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象的创建操作(同时付出一定开销)。一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费。

#include <memory>
#include <iostream>
#include <string>
 
int main()
{
    std::allocator<int> a1;   // int 的默认分配器
    int* a = a1.allocate(1);  // 一个 int 的空间
    a1.construct(a, 7);       // 构造 int
    std::cout << a[0] << '\n';
    a1.deallocate(a, 1);      // 解分配一个 int 的空间
 
    // string 的默认分配器
    std::allocator<std::string> a2;
 
    // 同上,但以 a1 的重绑定获取
    decltype(a1)::rebind<std::string>::other a2_1;
 
    // 同上,但通过 allocator_traits 由类型 a1 的重绑定获取
    std::allocator_traits<decltype(a1)>::rebind_alloc<std::string> a2_2;
 
    std::string* s = a2.allocate(2); // 2 个 string 的空间
 
    a2.construct(s, "foo");
    a2.construct(s + 1, "bar");
 
    std::cout << s[0] << ' ' << s[1] << '\n';
 
    a2.destroy(s);
    a2.destroy(s + 1);
    a2.deallocate(s, 2);
}
7
foo bar

boost::noncopyable

boost::noncopyable比较简单, 主要用于单例的情况.
通常情况下, 要写一个单例类就要在类的声明把它们的构造函数, 赋值函数, 析构函数, 复制构造函数隐藏到private或者protected之中, 每个类都这么做麻烦.
有noncopyable类, 只要让单例类直接继承noncopyable.
class noncopyable的基本思想是把构造函数和析构函数设置protected权限,这样子类可以调用,但是外面的类不能调用,那么当子类需要定义构造函数的时候不至于通不过编译。但是最关键的是noncopyable把复制构造函数和复制赋值函数做成了private,这就意味着除非子类定义自己的copy构造和赋值函数,否则在子类没有定义的情况下,外面的调用者是不能够通过赋值和copy构造等手段来产生一个新的子类对象的。
例子:https://www.cnblogs.com/zzyoucan/p/6659613.html

and or保留字

C++11具备的。
and 相当于 &&
or 相当于 ||

::开头

std::string
表示std命名空间下的 string类。
直接::开始,表示顶层命名空间(全局变量)std::string -> ::std::string 这样也可以。
::和 文件路径的 / 可以对照理解。

定义一个函数指针

typedef void (*comm_callback)(char*, int, void*); 

定义一个函数指针类型comm_callback
这个函数类型是这样:返回值void 参数:char,int,void
可以直接使用comm_callback定义一个函数指针
如:comm_callback pFun;

函数指针

什么是函数指针?

和数据类型一样,函数也有入口地址,该入口地址就是函数指针指向的地址。

什么时候要用函数指针?

比较多的时候是设置回调函数,窗口消息处理函数的时候。
你传进去自定义的函数名,对应的设置函数参数就是函数指针。
需要运行期绑定的时侯。即编程时可确定函数调用格式,但要运行时才能确定调哪个函数。

怎样使用函数指针?

int max(int x,int y){return (x>y? x:y);}
//声明函数指针
int (*ptr) (int,int);
//初始化函数指针
ptr=max;
//使用函数指针
std::cout<<(*ptr)(3,5);

typedef函数指针

如下,将NTSTATUS(WINAPI* )(HANDLE processHandle,PROCESSINFOCLASS processInformationClass,PVOID processInformation,ULONG processInformationLength,PULONG returnLength)替换为QUERYINFORMATIONPROCESS,即QUERYINFORMATIONPROCESS是一个函数指针。

typedef NTSTATUS(WINAPI* QUERYINFORMATIONPROCESS)(
	HANDLE processHandle,
	PROCESSINFOCLASS processInformationClass,
	PVOID processInformation,
	ULONG processInformationLength,
	PULONG returnLength);

回调函数

编程分为两类:系统编程(system programming)和应用编程(application programming)。所谓系统编程,简单来说,就是编写库;而应用编程就是利用写好的各种库来编写具某种功用的程序,也就是应用。系统程序员会给自己写的库留下一些接口,即API(application programming interface,应用编程接口),以供应用程序员使用。所以在抽象层的图示里,库位于应用的底下。当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。

打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)。

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
按照上面的说法,实现一个C Language的回调函数如下,

#include <stdio.h>
//回调函数
int ADD(int (*callback)(int,int), int a, int b){
  return (*callback)(a,b);//此处回调add函数...
}
//普通函数
int add(int a, int b){
  return a + b;
}
 
int main(void){
  printf("%d\n",add(1,2));
  printf("%d\n",ADD(add,1,2));
  return 0;
}
3
3

在这个例子中,可以看到,我们定义了一个callbak的函数指针,参数为两个int,返回值为int,通过调用函数地址来进行简单的相加运算。

可是,根据上面的例子,回调函数搞得这么麻烦,貌似并没有什么大作用.....纠结!
别纠结,来看一下库函数中的sort排序是怎么弄的。algorithm它提供了某些排序算法的实现(如冒泡排序、快速排序、shell排序、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,能让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调,如下:

#include <stdio.h>
#include <algorithm>
 
bool cmp(int a, int b){
  return a > b;
}
int main(void){
  int a[8] = {5,43,1,7,8,13,0,74};
  std::sort(a,a+10,cmp);//callback
  return 0;
}

简言之,回调函数一般是根据触发事件的不同而动态调用响应处理函数的行为。

void

中文翻译为“无类型”。常用在程序编写中对定义函数的参数类型、返回值、函数中指针类型进行声明。
void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。

allocator

new在内存分配上面有一些局限性,new的机制是将内存分配和对象构造组合在一起,同样的,delete也是将对象析构和内存释放组合在一起。
使用基本类型时消耗很小,但使用自定义类型时消耗很大,因为要去识别构造函数析构函数等,因此可以将内存分配和构造对象分开,将消耗大的对象构造推迟到使用时构造。

//定义名为a的allocator对象,可以分配内存或构造T类型的对象。
allocator<T> a; 
//分配原始的构造内存以保存T类型的n个对象.
a.allocate(n); 
//释放内存,在名为p的T*指针中包含的地址处保存T类型的n个对象。
a.deallocate( p, n ) 
//在T*指针p所指向的内存中构造一个新元素。运行T类型的复制构造函数用t初始化该对象
a.construct( p, t ) 
//运行T*指针p所指向的对象的析构函数。
a.destroy(p) 

operator()

函数对象:定义了调用操作符()的类对象。当用该对象调用此操作符时,其表现形式如同普通函数调用一般,因此取名叫函数对象。
与普通函数相比,函数对象比函数更加灵活,函数对象的优势:

  • 函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态;
  • 函数对象有自己特有的类型。我们可以传递相应的类型作为参数来实例化相应的模板,比如说带参数的函数形参。
class Sort  
{  
public:  
    bool operator() (const string &str1, const string &str2) const  //带两个参数
    {  
        return str1 > str2;  
    }  
};  

proxy与agent的区别

Proxy:服务器代理
Agent:代理服务器

二者都是面向不同类别用户并处理请求,Proxy强调服务器的安全性、隐秘性、面向的调用方为非用户,分解Server的压力;
Agent强调客户端(面向用户级)、本地化、统一收集信息,处理转换消息,发送至Server并更新本地状态。
Agent可以调用Proxy或直接调用Server.....

break和continue

break:跳出循环;
continue:跳过本次xun huan

posted @ 2020-05-12 07:29  多弗朗强哥  阅读(158)  评论(0编辑  收藏  举报