1、this
this是个特殊指针,在class里面指向当前对象,例如。
class Person
{
private:
char *name;
public:
void setName(char *name)
{
this->name = name;
}
};
2、::
- 用于指定当前函数属于某个类,例如setAge函数属于Person类的一个方法。
- 用于指定调用的函数是来自哪个命名空间,例如调用的是命名空间A里面的PrintVersion函数。
void Person::setAge(int age)
{
this->age = age;
}
A::PrintVersion();
3、 using用法:
- using A::Person; 声明使用命名空间A里面的Person类,不声明的话在使用类之前要加命名空间。
- using A::PrintVersion; 导入命名空间A里面的PrintVersion函数,不声明的话在调用函前面要加命名空间。
- 直接using namespace A; 一次性导入命名空间A涉及的类和函数。
4、cout
需要包含头文件iostream:#include ,不需要加.h,引用标准命名空间std。cout使用方法:cout<<"age = “<<age<<”,name = "<<name<<endl;这里的<<是重载了左移运算,达到字符串拼接的效果,enld是回车换行。
cout有时候的输出格式不是我们想要的,如下会输出i = d,需要加强转来解决。
uint8_t i = 100;
cout<<"i = "<<i<<endl;
5、构造函数
- 调用无参构造函数的方法:Person person;或者Person person(“zhangsan”,18);而Person person()只是声明一个函数,并不是定义一个类变量。
- 构造函数需要public属性。
- 指针方式定义类变量:Person *per = new Person;或者Person *per = new Person();这两种方式都是可以的,都会调用无参的构造函数。删除一个类:delete per。
- 构造函数传入默认参数,如Person(int age,char *name = “zhangsan”)。
- 一旦定义了有参构造函数,则必须定义一个无参构造函数。
6、析构函数
析构函数用户释放一些类里面动态分配的内存,函数定义格式为:类名(),例如Person(),析构函数无参。
7、类的默认函数
-
默认构造函数:一个无参的构造函数,函数的内容为空
-
默认的析构函数:函数的内容为空
-
默认的拷贝函数:是一个特殊的构造函数,其入参是该类的引用。当创建一个类的时候,如果传入的参数是同一个类的引用,那么会拷贝一份,注意传入的参数是类引用,而不是指针。例如:
Person *per = new Person(25,"Dokin"); Person *per2 = new Person(*per); 或 Person per(25,"Dokin"); Person per2(per);
类的默认拷贝函数在使用上有风险,因为创建出的类对象,其成员如果是指针类型,会和类引用指向同一空间,在内存分配和释放上就有可能产生被重复释放的问题,这种情况下需要重写拷贝函数。
8、构造函数调用顺序
-
按运行中定义对象的顺序调用构造函数,静态对象只调用一次构造函数,全局对象在main函数执行前被构造。
-
类定义中如果使用了别的类,那么会先按编码顺序依次调用别的类的构造函数,然后再调用本类的构造函数。
-
类的构造函数需要构造别的类时,在构造函数后面加:号,多个类用,隔开。如下面例子所示。注意,构造顺序和:后面的顺序无关,只和定义类成员的顺序有关,即下面的例子依然是father先被构造。析构的顺序则和构造的顺序刚好相反。
class Student { private: Person father; Person mother; int age; public: Student(){} Student(int age,char *father,int father_age,char *mother,int mother_age)\ :mother(mother,mother_age),father(father,father_age) { this->age = age; } }; int main(int argc,char **argv) { Student Dokin(15,"bill",40,"lily",39); return 0; }
9、类的静态成员和方法
类里面可以使用static对成员和方法进行修饰,表明该成员或方法属于类,而不是属于类的实例对象。
类的静态成员即使是private权限,也可以通过类名来直接访问。
静态方法只能访问静态变量,不能访问非静态变量,因为静态变量属于类,而非静态变量属于类的实例对象。
类的实例对象也可以访问类的静态方法。
类的静态方法可以在类里面声明然后类的外面定义,例如:
class Person
{
private:
static int cnt;
public:
static int getCnt();
/* ... */
}
int Person::getCnt()
{
return Person::cnt;
}
类定义时并没有分配内存空间,因此需要在外部进行定义和初始化,下面是一个例子。
class Person
{
private:
char *name;
int age;
static int cnt;
public:
Person()
{
cnt ++;
this->name = NULL;
}
~Person()
{
cnt --;
}
static int getCnt()
{
return cnt;
}
}
int Person::cnt = 0;
int main(int argc,char **argv)
{
Person per1;
Person per2;
cout<<"cnt = "<<Person::getCnt()<<endl;
}
10、友元函数
在类的定义中,可以用friend关键字来声明一个友元函数,友元函数可以直接访问所在类的私有成员,从而提高效率。下面是一个例子。
#include <iostream>
using namespace std;
class Point
{
private:
int x;
int y;
public:
Point(){}
Point(int x,int y):x(x),y(y){}
void print()
{
cout<<"x = "<<x<<",y = "<<y<<endl;
}
friend Point addPoint(Point &p1,Point &p2);
};
Point addPoint(Point &p1,Point &p2)
{
Point point;
point.x = p1.x + p2.x;
point.y = p1.y + p2.y;
return point;
}
int main(int argc,char **argv)
{
Point p1(1,2);
Point p2(3,4);
Point p3 = addPoint(p1,p2);
p3.print();
}
11、const成员函数
在成员函数后面加上const表示此函数没有修改操作,如void printInfo(void)const。
12、运算符重载
例如重载++号运算,那么函数名为Point operator++(Point p),通过入参的不同可以实现运算符的不同功能,例如以下++的实现。
/* 实现++p */
Point operator++(Point &p)
{
p.x += 1;
p.y += 1;
return p;
}
/* 实现p++ */
Point operator++(Point &p, int a)
{
Point temp;
temp = p;
p.x += 1;
p.y += 1;
return temp;
}
上述函数通过函数入参的不同实现了函数重载,实现了前++和后++。
注意Point operator++(Point &p)函数中定义了返回了一个临时变量p,这会造成临时变量p的构造函数和析构函数被调用,从而影响函数执行效率,最佳的实现方式如下,返回一个引用,这样就不会有临时变量,也就不会调用构造函数和析构函数了。
/* 实现++p */
Point& operator++(Point &p)
{
p.x += 1;
p.y += 1;
return p;
}
重载=号运算符可以解决值拷贝造成的问题:拷贝类的指针成员,导致两个类的成员指向同一块地址。重载=号可以在重载函数里对指针成员进行内存申请和销毁,避免指向同一地址。
以下操作的结果是不同的:p2 = p1会调用默认的拷贝函数,而不是调用operator=重载函数。先定义p3,然后p3=p1这样才会调用operator=重载函数。
Person p1;
Person p2 = p1;
Person p3;
p3 = p1;