侧边栏壁纸
博主头像
lmg博主等级

  • 累计撰写 55 篇文章
  • 累计创建 6 个标签
  • 累计收到 2 条评论
标签搜索

C++ Coding Skill

lmg
lmg
2023-07-07 / 0 评论 / 0 点赞 / 706 阅读 / 2,944 字
温馨提示:
本文最后更新于 2023-08-05,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. placement new

所谓placement new就是在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。
举例来说:

class foo{};
foo* pfoo = new foo;

pfoo指向的对象的地址你是不能决定的,因为new已经为你做了这些工作。第一步分配内存,第二步调用类的构造函数。(new = allocate memory + call constructor)

char* ptr = new char[sizeof(myClass)*10]
A* p=new (ptr)A;
申请空间,其中ptr就是程序员指定的内存首地址。

placement new的好处:
1)在已分配好的内存上进行对象的构建,构建速度快。
2)已分配好的内存可以反复利用,有效的避免内存碎片问题。

placement new还可以解决的一个问题是建立带参数的构造函数对象数组。

class CPong
{
public:
    CPong( int m ) : v(m) { std::cout << "CPong ctor." << std::endl; }

private:
    int v;
};

char* pong = new char[ sizeof(CPong) * 10 ];
CPong* pp = (CPong*)pong;

for ( int i=0; i<10; ++i )
{
    new (pp+i)CPong(i);
}

for ( int j=0; j<10; ++j )
{
    pp[j].~CPong();
}

delete [] pong;
#include <iostream>
#include <stdio.h>
using namespace std;

class Empty {};
class X{
    int i;
    Empty e;
};
class Y{
    int i;
    [[msvc::no_unique_addr]] Empty e;
};

int main() {
    cout <<"hello world"<<endl;
    printf("sizeof(int):%d\n", sizeof(int));
    printf("sizeof(X):%d\n", sizeof(X));
    printf("sizeof(Y):%d\n", sizeof(Y));
    return 0;
}

野指针

vector<int*> vec;
  int* p1 = new int(1);
  int* p2 = p1;

  vec.push_back(p1);
  vec.push_back(p2);

  printf("before delete p1:%p, p2:%p\n", p1, p2);
  delete p1;
  p1 = NULL;
  printf("after delete p1:%p, p2:%p\n", p1, p2);
  static_assert(p2 != NULL);// 只是把p1指向NULL, p2还是指向原来的内存(野指针)
  static_assert(vec.at[0] != NULL);// vec[0]不等于NULL, 因为拷贝到vec[0]的是内存地址, 同野指针

函数指针和函数对象

函数指针:
是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址。函数指针主要由以下两方面的用途:调用函数和用作函数参数。
一般函数的声明为:
int func ( int x );
而一个函数指针的声明方法为:
int (func) (int x);
前面的那个(func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针的函数

int add(int x, int y) {
   return x+y;
}
int main() {
   int (*fa)(int a, int b); // 声明一个函数指针
   fa = add; // 同fa=&add;
   printf("&add,%p, %p\n",add, &add);
   fa(1,2);
 }

也可以用作函数参数
比如C 库函数 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void , const void)) 对数组进行排序。

函数对象:

C函数对象实质上是操作符重载,实现了对()操作符的重载。C函数对象不是函数指针。但是,在程序代码中,它的调用方式与函数指针一样,后面加个括号就可以了。

struct FuncClass {
  int operator()(int x, int y){
     return x+y;
  }
};

int main() {
   FuncClass fc;
   printf("result of fc(1,2):%d\n",fc(1,2));
   return 0;
}

函数对象与函数指针比较
函数对象可以把附加对象保存在函数对象中是它最大的优点。它的弱势也很明显,它虽然用起来象函数指针,但毕竟不是真正的函数指针。在使用函数指针的场合中,它就无能为力了。例如,你不能将函数对象传给qsort函数!因为它只接受函数指针。

Lambda表达式

C++11 function函数对象

回调函数 callback funtion

A “callback” is any function that is called by another function which takes the first function as a parameter。
通过函数指针调用的函数,就叫做回调函数

为什么要使用回调函数?
很多朋友可能会想,为什么不像普通函数调用那样,在回调的地方直接写函数的名字呢?这样不也可以吗?为什么非得用回调函数呢?有这个想法很好,因为在网上看到解析回调函数的很多例子,其实完全可以用普通函数调用来实现的。要回答这个问题,我们先来了解一下回到函数的好处和作用,那就是解耦,对,就是这么简单的答案,就是因为这个特点,普通函数代替不了回调函数。所以,在我眼里,这才是回调函数最大的特点

#include<stdio.h>
#include<softwareLib.h> // 包含Library Function所在读得Software library库的头文件

int Callback() // Callback Function
{
    // TODO
    return 0;
}
int main() // Main program
{
    // TODO
    Library(Callback);
    // TODO
    return 0;
}

乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。

0

评论区