单引号 :
1. 继承
class 派生类名 :继承方式 基类名
如:
class BaseClass
{
public :
BaseClass();// 构造函数,无返回类型,可以有参数列表,这里省去
~BaseClass();// 析构函数
}
class SubClass:public BaseClass()
{
}
2. 初始化参数列表
使用初始化列表的原因
初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。
主要是性能问题,对于内置类型,如int, double等,使用初始化列表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。
所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表。
必须使用初始化列表的时候
除了性能问题之外,有些时候初始化列表是不可或缺的,以下几种情况时必须使用初始化列表
1.常量成员,const修饰的类成员变量,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
3.没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化
class Date
{
public:
Date(int year)
: _year(year)
{}
private:
int _year;
};
class Time
{
public:
Time(Date& d)
{
_d = d;
}
private:
Date _d;
};
以上代码无法通过编译,因为Time的构造函数中 _d = d这一行实际上分成两步执行:
首先调用Date的默认构造函数来初始化_d
由于Date没有默认的构造函数,所以无法执行初始化_d,故而编译错误。
正确的代码如下,使用初始化列表代替赋值操作
class Time
{
public:
Time(Date& d)
: _d(d)
{}
private:
Date _d;
};
成员变量顺序
成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的。
如下代码:
class Fun
{
public:
//先初始化_x,再初始化_y
Fun(int tmp)
: _x(tmp)
, _y(_x)
{}
private:
int _x;
int _y;
};
再看下面的代码:
class Fun
{
public:
//_y未定义
Fun(int tmp)
: _y(tmp)
, _x(_y)
{}
private:
int _x;
int _y;
};
这里 _x 的值是未定义的,虽然 _y 在初始化列表里面出现在 _x 前面,但是 _x 先于 _y 定义,所以必须先初始化 _x,而 _x 由 _y 初始化,此时 _y 尚未初始化,所以导致 _x 的值未定义。一个好的习惯是,按照成员定义的顺序进行初始化 。
参数列表初始化示例代码
示例1
TEST( int AA, int BB ):a(AA),b(BB)
{
}
//相当于
TEST( int AA, int BB)
{
a=AA;
b=BB;
}
示例2
class String
{
public:
String(const String& s)
:_str(new char[strlen(s._str)+1])
{
strcpy(_str,s._str);
}
~String()
{
delete [] _str;
}
private:
char *_str;
};
//相当于
class String
{
public:
String(const String& s)
{
_str=new char[strlen(s._str)+1];
strcpy(_str,s._str);
}
~String()
{
delete [] _str;
}
private:
char *_str;
};
示例3
class Point
{
private:
int X,Y;
public:
Point(int xx=0,int yy=0)//类Point的构造函数
{
X=xx;
Y=yy;
}
//拷贝构造函数,Point(Point &p)表示p是point类的一个对象,
//而&p则是这个对象的引用(也就是这个对象的地址)。这是拷贝构造函数的规定,不能变。
Point(Point &p);
int GetX(){return X;}
int GetY(){return Y;}
};
Point::Point(Point &p)//拷贝构造函数的实现,::来表示他是类的构造函数(成员函数)
{
X=p.X;
Y=p.Y;
cout<<"Point拷贝构造函数被调用!"<<endl;
}
class Line
{
private:
Point p1,p2;
double len;
public:
Line(Point xp1,Point yp2);//类Line的构造函数
Line(Line &L);
double GetLen(){return len;}
};
Line::Line(Point xp1,Point yp2):p1(xp1),p2(yp2)//初始化列表方式
{
cout<<"Line构造函数被调用!"<<endl;
double x=double(p1.GetX()-p2.GetX());
double y=double(p1.GetY()-p2.GetY());
len=sqrt(x*x+y*y);
}
Line::Line(Line &L):p1(L.p1),p2(L.p2)
{
cout<<"Line拷贝构造函数被调用!"<<endl;
len=L.len;
}
void main()
{
Point myp1(1,1),myp2(4,5);
Line line(myp1,myp2);
Line line2(line);
cout<<"The Length of the line is:";
cout<<line.GetLen()<<endl;
cout<<"The Length of the line2 is:";
cout<<line2.GetLen()<<endl;
}
3. 位域
该种形式出现于结构体或共用体的定义中,是位域定义的标准形式。
其使用方式为
struct name
{
type var_name : n;
};
含义为,在结构体name汇总,成员变量var_name占用空间为n位。
n为正整数,其值必须小于type类型占用的位数。比如type如果是int,占4字节32位,那么n必须是1~31之间的整数。
对于位域类型的成员,在赋值时如果实际值超过n位所能表达的范围,那么超出部分将会被截掉,只保存低位值。如int var:4,本身只有4位的空间,如果赋值var = 20, 由于20的二进制值为10100,实际为五位,这时var实际被赋值的就是低四位,0100,即4。
4. 遍历
vector<vector<int>>& rectangles
for(vector<int> rectangle : rectangles) {
//获取坐标
left = min(left, rectangle[0]);
low = min(low, rectangle[1]);
right = max(right, rectangle[2]);
high = max(high, rectangle[3]);
}
双引号 ::
1. 类作用域,用来标明类的变量、函数
作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分
如:A,B表示两个类,在A,B中都有成员member。那么
A::member就表示类A中的成员member
B::member就表示类B中的成员member
Human::setName(char* name);
2. 命名空间作用域,用来注明所使用的类、函数属于哪一个命名空间的
std::cout << "Hello World" << std::endl;
3. 全局作用域,用来区分局部、全局的。
最容易被忽视的一种,很多时候写了一个全局函数或者想要调用一个全局函数,却发现IDE或者Editor找不到该函数,原因是因为局部函数与想要调用的全局函数名字一样,然后找了很久也找不到原因,甚至放弃解决的。其实原因就是因为 【局部变量/函数】 与 【全局变量/函数】 的名字相同,IDE无法区分,这时候加上 :: 就可以调用到全局函数,访问到全局变量了。
全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分如:
char zhou; //全局变量
void sleep()
{
char zhou; //局部变量
char(局部变量) = char(局部变量) *char(局部变量) ;
::char(全局变量) =::char(全局变量) *char(局部变量);
}
示例
Linux下串口打开、关闭的api
// fcntl.h文件下的全局函数open
open (const char *__path, int __oflag, ...)
// unistd.h文件下的全局函数
extern int close (int __fd);
由于每次找api是一件非常浪费coding时间,而且是没多大意义的事情,我现在要将这个函数封装成一个我自己的个人串口库WzSerialPort.h、WzSerialPort.cpp
// WzSerialPort.h
class WzSerialPort
{
public:
// ...
bool open();
void close();
// ...
};
注意以下的cpp文件,如果没有 :: 则会报错误,因为WzSerialPort库中有函数open和close,跟全局函数open和close名字相同,如果不做全局与局部的区分,则无法调用到全局函数
// WzSerialPort.cpp
bool WzSerialPort::open()
{
if( ::open(portname,O_RDWR|O_NOCTTY|O_NONBLOCK) != -1 )
return true;
else
return false;
}
void WzSerialPort::close()
{
::close(fd);
}