赞
踩
大纲
- 前置知识:C++中类(class)的相关知识
- 前置知识:C++中模板(template)的相关知识
- opencv中Vec类的定义及源码
- 引申:opencv中的宏定义如CV_8UC3
一、C++中的类
“类”是对对象的抽象,C++中的类实际上就是对一类具有相同特性的对象的交集,类相当于一种新的数据类型,数据类型不占用存储空间,用类型定义一个实体的时候,才会为它分配存储空间。
1.类的定义如下:
class 类名
{
成员类表
};
成员列表是类成员的集合,成员可以无限多,也可以没有;可以是数据,也可以是函数,成员函数如普通函数一样可以进行重载或者带默认参数。如果类成员中有函数的话,有以下两种定义方式:
在类内部定义函数体
class 类名
{
返回类型 函数名(形参列表)
{
函数体
} };
在类外部定义函数体
class 类名
{
返回类型 函数名(形参列表);
};
返回类型 类名 :: 函数名(形参列表)
{
函数体
}
前一种定义方式相当于内联函数,调用速度快,但是会占用额外的内存,后一种方式则为普通的外部函数,建议使用第二种方式定义。
如果使用后一种方式定义的话,需要注意C++中设置默认形参的动作之只能有一次,在类中声明时设置了默认参数,外部定义时就不能再重复设置。
class Test
{
public:
void Sum(int a=0,int b=0);
};
void Test::Sum(int a,int b)
//void Test::Sum(int a=0,int b=0) 错误
{
cout<<a+b;
}
2.类成员的访问控制
类的每个成员都有着自己的访问属性,包括“public"、”private"、"protected
“public"通常包含成员函数、”private“通常包括一般的变量。对于外部用户像是全局函数、另一个类的成员函数,只能通过"public"来访问类的成员。如:
#include <iostream> using namespace std; class Test { public: Test() {} Test(int x, int y) :a(x), b(y) {} void Sum(); private: int a, b; void minus() }; void Test::Sum() { cout << a + b; } void Test::minus() { cout << b - a; } int main() { Test test2(3, 4); test2.Sum(); //可以运行 cout << test2.a; //无法访问 test2.minus() //无法访问 return 0; }
从而类的定义扩展为:
class 类名
{
public:
公有的数据成员和成员函数
protected:
保护的数据成员和成员函数
private:
私有的数据成员和成员函数
};
3.类成员的初始化
建立一个类的对象时,需要对对象进行初始化,因为在类里面是不能对成员进行进行初始化的(类是数据类型,不占用存储空间),初始化的过程通过构造函数来实现。构造函数是一个没有返回值、和类同名的成员函数,由于创建对象通常是在外部进行的,所以构造函数申明为”public",例下:
#include <iostream> using namespace std; class Test { public: Test (); Test (int x,int y); void Sum(); private: int a,b; }; Test::Test() { } Test::Test(int x,int y) { a=x; b=y; } void Test::Sum() { cout<<a+b; } int main() { Test test(3,4); test.Sum(); return 0; }
第一个“Test()“为默认构造函数也叫无参构造函数,它没有形参,可以在后续过程中赋值而不需要立即初始化。使用方法即:
TEST test
如果类中没有默认构造函数的话会报错,但IDE通常情况下会自动生成默认构造函数(实际上是4种特定情况,参见:自动生成默认构造函数情况),但如果自己定义了一个有参的构造函数就如“TEST (int x int y)",那IDE就不会自动再生成。也就是说,如果类中无默认构造函数,也可以通过”TEST test“初始化,一旦类中只存在有参构造函数,就必须带参初始化。、
构造函数初始化列表
之前介绍的构造函数是利用函数体内赋值的方式完成初始化,但更常用的一种方法其实是构造函数初始化列表,如:
#include <iostream> using namespace std; class Test { public: Test (); Test (int x,int y); void Sum(); private: int a,b; }; Test::Test() { } Test::Test(int x,int y):a(x),b(y) //构造初始化列表而非赋值 { } void Test::Sum() { cout<<a+b; } int main() { Test test(3,4); test.Sum(); return 0; }
也可以更简洁的表述为:
class Test
{
public:
Test () {}
Test (int x,int y):a(x),b(y) {} //直接在类内部完成定义
void Sum();
private:
int a,b;
};
这种方式较函数内赋值一般情况下没有区别,但如果需要初始化的是类类型的成员,就需要使用构建函数初始化列表的方法了。如:
#include <iostream> using namespace std; class Test { public: Test () {} Test (int x,int y):a(x),b(y) {} void Sum(); private: int a,b; }; void Test::Sum() { cout<<a+b; } class AnotherTest { public: AnotherTest(int i,int j):test(i,j) {test.Sum();} private: Test test; }; int main() { AnotherTest test(3,4); return 0; }
这里Anothertest类中含有一个数据类型为Test的成员,因此需要使用
初始化列表,注意因为在AnotherTest的构造函数里调用了Test类中的Sum成员函数,所以会有输出”7“,如果直接写test.test.Sum()是不会有输出值的,第二个test属于private类型,无法访问。
复制构造函数
复制构造函数顾名思义,通过一个已经存在的对象对另一个对象赋值初始化,常用形式为:
类名 (const 类名& obj)
{
函数体
}
例如:
#include <iostream> using namespace std; class Test { public: Test () {} Test (int x ,int y):a(x),b(y) {} Test (const Test& t):a(t.a),b(t.b) {} void Sum(); private: int a,b; }; void Test::Sum() { cout<<a+b; } int main() { Test test1(3,4); Test test2 = test1; test2.Sum(); return 0; }
如果不定义复制构造函数,以上对象也可以这样进行初始化,原因就是系统也会自己生成一个复制构造函数。
类的继承
类的继承允许在已有类的基础上建立新类,继承方式,c++提供了三中继承方式。
public(公有继承):基类中的公有和保护成员保持原属性,私有成员为基类私有。
private(私有继承):基类中的所有成员在派生类中都是私有的。
protected(保护继承):基类的公有成员和保护成员在派生类中成了保护成员,私有成员仍为基类私有。
继承的写法如下:
class 子类名:public/ptivate/prtected 父类名
{
成员
}
例矩形对平行四边形的继承:
#include <iostream> using namespace std; class Parallelogram { public: Parallelogram(int a,int b):length(a),width(b) {} int getLength(){return length;} int getWidth() {return width;} private: int length,width; }; class Rectangle : public Parallelogram //公有继承 { public: Rectangle(int a,int b):Parallelogram(a,b) {} //先对基类中的数据成员进行初始化 void Area() //计算面积 { cout<<getLength()*getWidth(); } }; int main() { Rectangle r(3,4); r.Area(); return 0; }
至此,“类”相关知识介绍完毕。
二、C++中的模板
如果说类是对对象的抽象,那么模板就是对类的抽象,模板相当于具有相同特性的类的交集,它声明了一种类的模板,并且提供了一个或多个的虚拟类型参数,从而给出不同数据类型的同一特性类。
模板的定义
常用形式为:
template<class/typename 虚拟类型1, class/typename 虚拟类型2…>
class 类名
{
成员
}
以下给出了一个适用于float和int型比较的类模板。
template <class numtype> //声明一个模板,虚拟类型名为numtype class Compare //类模板名为Compare { public : Compare(numtype a,numtype b) { x=a;y=b; } numtype max( ) { return (x>y)?x:y; } numtype min( ) { return (x<y)?x:y; } private : numtype x,y; };
需要注意这里的成员函数是在类里面定义的,如果是在类外定义的话不能使用一般类成员函数的定义方法
numtype Compare::max( ) {…}
而应该先声明类模板:
template <class numtype> numtype Compare<numtype>::max( ) { return (x>y)?x:y; }
- 1
- 2
- 3
- 4
- 5
模板的初始化
使用模板定义对象时的形式为:
类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
如:Compare<int> cmp; Compare<int> cmp(3,7);
至此,所需“模板”知识介绍完毕。
三、opencv中的 Vec类
opencv中经常使用Vec+数字+字母来定义变量,像是Vec4b,Vec3s,其实这就是一个向量模板类,首先它在任何时刻都是一个一维矩阵(即列向量),其次它的元素数据类型可变。这一模板类是对与Mat类的继承,其源码如下;
//【1】下面是OpenCv中定义---定义---向量模板类----的源代码 //【2】一个拥有非类型模板形参的---类模板-----向量模板类 template<typename _Tp, int cn> class Vec : public Matx<_Tp, cn, 1> { public: typedef _Tp value_type; enum { depth = DataDepth<_Tp>::value, channels = cn, type = CV_MAKETYPE(depth, channels) }; //! default constructor //【1】向量类的默认构造函数 Vec(); //【1】向量类有参构造函数 Vec(_Tp v0); //!< 1-element vector constructor Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2); //!< 3-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 4-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 5-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 6-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 7-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 8-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 9-element vector constructor Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 10-element vector constructor explicit Vec(const _Tp* values); //保证函数显式调用 Vec(const Vec<_Tp, cn>& v); static Vec all(_Tp alpha); //! per-element multiplication Vec mul(const Vec<_Tp, cn>& v) const; //! conjugation (makes sense for complex numbers and quaternions) Vec conj() const; /*! cross product of the two 3D vectors. For other dimensionalities the exception is raised */ Vec cross(const Vec& v) const; //! convertion to another data type template<typename T2> operator Vec<T2, cn>() const; //! conversion to 4-element CvScalar. operator CvScalar() const; /*! element access */ const _Tp& operator [](int i) const; _Tp& operator[](int i); const _Tp& operator ()(int i) const; _Tp& operator ()(int i); Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp); Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp); template<typename _T2> Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp); }; //【3】用typedef关键字给---向量类模板----template<typename _Tp, int cn> class Vec //【1】向量模板类Vec的实例化,并且给相应实例的Vec向量模板类实例---指定新的名字 //【1】Vec2b--这是一个具体的--类类型---这个类类型实例话的类对象表示如下所示: //【1】Vec2b---表示每个Vec2b对象中,可以存储2个char(字符型)数据 typedef Vec<uchar, 2> Vec2b; 、 //【2】Vec3b---表示每一个Vec3b对象中,可以存储3个char(字符型)数据,比如可以用这样的对象,去存储RGB图像中的一个像素点 typedef Vec<uchar, 3> Vec3b; //【3】Vec4b---表示每一个Vec4b对象中,可以存储4个字符型数据,可以用这样的类对象去存储---4通道RGB+Alpha的图像中的像素点 typedef Vec<uchar, 4> Vec4b; //【1】Vec2s---表示这个类的每一个类对象,可以存储2个short int(短整型)的数据 typedef Vec<short, 2> Vec2s; typedef Vec<short, 3> Vec3s; typedef Vec<short, 4> Vec4s; typedef Vec<ushort, 2> Vec2w; typedef Vec<ushort, 3> Vec3w; typedef Vec<ushort, 4> Vec4w; typedef Vec<int, 2> Vec2i; typedef Vec<int, 3> Vec3i; typedef Vec<int, 4> Vec4i; typedef Vec<int, 6> Vec6i; typedef Vec<int, 8> Vec8i; typedef Vec<float, 2> Vec2f; typedef Vec<float, 3> Vec3f; typedef Vec<float, 4> Vec4f; typedef Vec<float, 6> Vec6f; typedef Vec<double, 2> Vec2d; typedef Vec<double, 3> Vec3d; typedef Vec<double, 4> Vec4d; typedef Vec<double, 6> Vec6d;
至此,opencv中的Vec模板类介绍完毕。
四、Opencv中的宏定义
使用Opencv时常常看到的字母加数字缩写除了Vec模板类,还有就是宏定义了,它经常性的用于Mat的初始化,其具体形式及含义如下:
CV+位深+数据类型缩写+U+通道数目
数据类型缩写有:
S–代表—signed int—有符号整形
U–代表–unsigned int–无符号整形
F–代表–float---------单精度浮点型
其具体含义也就不难理解,CV16UC4就表示16位无符号整型三通道矩阵。
如果我们要使用到Vec类来对使用这种宏定义初始化的Mat赋值时,需要注意Vec类要和宏定义相互对应,像是:
Mat tempImg1(480,640,CV_8UC3);
Vec3b& test = temp.at<Vec3b>(i, j);
由于此处定义的Mat为8位深3通道,所以对应Vec模板类为可容纳3(3通道
个字符型数据(8位)的Vec3b
再如:
Mat tempImg1(480,640,CV_16UC4);
Vec4w& test = temp.at<Vec4w>(i, j);
由于此处定义的Mat为16位深4通道,所以对应Vec模板类为可容纳4个(4通道)无符号整型数据(16位)的Vec4w
参考文献:
C++中类的介绍
C++template模板使用方法
OpenCV 中的 Scalar 类、Vec类
详解 c++ 关键字 explicit
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。