C/C++数据对齐机制

(1)对于n字节的元素(n=2,4,8,…),它的首地址能被n整除,才能获得最好的性能;

(2)假设len为结构体中长度最长的变量,size为CPU(处理器)的位数,对齐规则:

若len < size,则以len为单位对齐

若len >= size,则以size为单位对齐

opencv掩膜理解

在OpenCV中我们经常会遇到一个名字:Mask(掩膜)。很多函数都使用到它,那么这个Mask到底什么呢?

比如我要对一幅图进行抠图操作,这就要用到Mask了,那我就以抠图为例,解释Mask在里面的作用。整体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat image, mask;
Rect r1(100, 100, 250, 300);
Mat img1, img2, img3, img4;
image = imread("img.jpg");
mask = Mat::zeros(image.size(), CV_8UC1);
mask(r1).setTo(255);
img1 = image(r1);
image.copyTo(img2, mask);

image.copyTo(img3);
img3.setTo(0, mask);


imshow("Image sequence", image);
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);
imshow("mask", mask);

waitKey();
return 0;
}

其中:

1
2
mask = Mat::zeros(image.size(), CV_8UC1); 
mask(r1).setTo(255); //r1是设置好的感兴趣区域

解释一下上面两句的操作。

  • 第一步建立与原图一样大小的mask图像,并将所有像素初始化为0,因此全图成了一张全黑色图。
  • 第二步将mask图中的r1区域的所有像素值设置为255,也就是整个r1区域变成了白色。
    这样就能得到Mask图像了。

pic

注意这句,哪个图像拷贝到哪个图像?

1
image.copyTo(img2, mask);

当然是原始图image拷贝到目的图img2上啦。
其实拷贝的动作完整版本是这样的:

原图(image)与掩膜(mask)进行与运算后得到了结果图(img2)。

掩膜的与运算其实就是原图中的每个像素和掩膜中的每个对应像素进行与运算。比如1 & 1 = 1;1 & 0 = 0;

比如一个3x3的图像与3 * 3的掩膜进行运算,得到的结果图像就是:

pic

所以运行结果如下
pic

下面两句代码所做的事情跟上面的差不多,首先将原始图image拷贝一份给img3,然后img3将mask白色区域设置为0(黑色),好比如果mask中像素非0的,我就把我图像对应的那个点的像素值设置为0,否则啥也不做。伪代码是if mask(i,j)>0 then img3(i,j)=0。

1
2
image.copyTo(img3);
img3.setTo(0, mask);

pic

如果要得到扣出的目标区域,这样写就可以了

1
img1 = image(r1);

运行结果:

pic

C++虚函数笔记(2)

本文转载自CSDN C++虚函数表解析

虚函数

虚函数就是用virtual来修饰的函数。虚函数是实现C++多态的基础。

虚函数表

每个类都会为自己类的虚函数创建一个表,来存放类内部的虚函数成员。

在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

实例

1
2
3
4
5
6
7
8
9
10

class Base {
public:
virtual void f() { cout << "Base::f" << endl; }

virtual void g() { cout << "Base::g" << endl; }

virtual void h() { cout << "Base::h" << endl; }

};

按照上面的说法,我们可以通过Base的实例来得到虚函数表。 下面是实际例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef void(*Fun)(void);

Base b;

Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;

cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;

// Invoke the first virtual function

pFun = (Fun)*((int*)*(int*)(&b));

pFun();
实际运行经果如下:

虚函数表地址:0012FED4

虚函数表 — 第一个函数地址:0044F148

Base::f

通过这个示例,我们可以看到,我们可以通过强行把&b转成int ,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下:

1
2
3
(Fun)*((int*)*(int*)(&b)+0);  // Base::f()
(Fun)*((int*)*(int*)(&b)+1); // Base::g()
(Fun)*((int*)*(int*)(&b)+2); // Base::h()
示例图如下所示:

pic

在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值如果是1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

一般继承

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

pic

对于实例:Derive d; 的虚函数表如下:
pic

当派生类覆盖了基类的函数f()时,实例的虚函数表则是这样:
pic

这样,我们就可以看到对于下面这样的程序,

1
2
3

Base *b = new Derive();
b->f();

由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

多重继承

假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。

pic

对于子类实例中的虚函数表,是下面这个样子:

pic

我们可以看到:

  • 每个父类都有自己的虚表。

  • 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)

这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

当有函数覆盖时,即重载了f(),实例的虚函数表:

pic

我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。

opencv VS2017项目通用设置

opencv学习笔记

在配置好Visual Studio环境时,需要对项目进行包含目录和动态库的配置,目的是让IDE找到库的位置。一开始是每次创建项目都要重新配置项目,非常麻烦。后来发现在配置的时候疏忽了一个点。在属性管理器面板中,需要对Debug|win32或者Debug|x64展开里的Microsoft. Cpp. Winxx. user配置才能全局生效。

C++虚函数笔记(1)

C++虚函数简单介绍

如果派生类在继承了基类后直接重写了基类的某个方法,当使用基类指针指向派生类实例时,调用的方法其实是调用的父类的方法。要实现多态,则需要使用虚函数。(动态多态性)

内存泄露

如果同样是上面的情况,需要释放两个指针指向的空间时,则会造成内存泄漏,因为在默认情况下调用的析构函数是基类的析构函数,而没有调用子类的析构函数。

1
2
3
4
5
6

Shape *shape1=new Circle(4.0);
Shape *shape2=new Rect(3.0,5.0);

delete shape1;
delete shape2;

如果在基类的析构函数声明虚函数,这样基类指针指向的是哪个对象,销毁的时候派生类的析构函数也会执行,再执行基类的析构函数。

C变量存储

  • 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。

  • 堆,就是那些由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。堆可以动态地扩展和收缩。

  • 自由存储区,就是那些由 malloc 等分配的内存块,他和堆是十分相似的,不过它是用 free 来结束自己的生命的。

  • 全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放),在 C++ 里面没有这个区分了,他们共同占用同一块内存区。

  • 常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)

重新搞了个博客,以后尽量在这里更新所有的文章。