目录
类就是将相似的数据以及方法封装在一起,加以权限区分,外部只能通过类提供的公共方法来访问类中的私有数据。类是C语言和C++的一个很大的区别,C++是基于对象展开的编程,C语言是基于过程的。
【类的概述】
1.定义一个类
class 类名
{
};
类中还有一个很重要的概念就是权限的问题,C++中类中权限分为public、protected、private(如果没有划分权限,默认都是私有的),三个权限决定了类中的数据在外界可否访问以及能否从该类中继承数据
访问属性 | 属性 | 对象外部 | 继承性 |
public | 公有的 | 可访问 | 可继承 |
protected | 保护的 | 不可访问 | 可继承 |
private | 私有的 | 不可访问 | 不可继承 |
2.在类外定义函数
我们在类中声明成员函数,但是如果在里面定义的话,会显得类十分的臃肿,我们可以在类外去定义函数
2.1 类外定义
class A
{
public:
int a;
int b;
void fun(void);
};
//类外实现要加作用域
void A::fun()
{
}
2.2 其他文件定义
在项目中右击添加新文件,选中C++/C++ Class,然后再class name中添加类的名字即可,头文件中一般用来定义类和函数,CPP文件中实现函数
【构造函数】
构造函数是一种特殊的函数,只要有类实例化对象,那么必须会调用构造函数,一个对象如果没有经过初始化那么是不安全的。构造函数会在实例化对象的时候自动调用,分为有参构造以及无参构造。
1.构造函数的创建
构造函数没有返回值,并且函数名必须和类名称完全相同
class Stu
{
public:
int num;
//构造函数
Stu()
{
//....
}
};
构造函数可以有形参,那么也就可以实现函数重载,同时构造函数的权限大部分都是public,只有在实现c++单例模式下构造函数才是私有权限的。
2.构造函数的调用
如果在实例化对象的时候,没有传递参数进去,那么调用的就是无参构造,如果传递了参数进去,那么调用的就是有参构造,具体的调用方法分为很多种。
class Data
{
public:
int a;
}
//显式调用无参构造
Data ob1 = Data();
//隐式调用无参构造
Data ob2;
//显式调用有参构造
Data ob3 = Data(100);
//隐式调用有参构造
Data ob4(100);
//如果类只有一个成员变量,会有如下的特殊情况
//隐式转换
Data ob5 = 100;
【析构函数】
当一个对象的生命周期结束的时候,会自动调用析构函数。
析构函数的函数名和类的函数名完全相同,但是前面加了~,因为析构函数没有必要传入参数,所以不用传参,那么也就没有函数重载
一般情况下,类的析构函数是可以空着的,但是如果类中有成员是指针,那么就需要手动在析构函数中释放指针指向的空间,否则如果指针被释放,该空间就无法在访问,造成内存泄漏。
1.析构函数的定义
正常情况下,析构函数的定义
class Data
{
public:
//析构函数不存在成员指针的情况下
~Data()
{
}
}
如果类中存在指针成员
class Data
{
public:
int* add;
//析构函数不存在成员指针的情况下
~Data()
{
if( add != NULL )
{
//一般指针都是指向堆区空间,释放
free(add);
//把指针置为空
add = NULL;
}
}
}
【拷贝构造】
1.拷贝构造的定义
拷贝构造本质上还是有参构造,但是传入的参数是同类型的变量,也就是用旧的对象来初始化新的对象。
class Data
{
public:
Data(const Data& obj)
{
}
}
2.有参、无参、拷贝构造关系
如果调用了有参构造或者拷贝构造,那么系统会屏蔽无参构造
如果调用了无参构造或者有参构造,不会屏蔽拷贝构造
3.拷贝构造的几种调用形式
3.1 旧对象初始化新对象调用拷贝构造
class Data
{
public:
int a;
Data(int b)
{
a = b;
}
Data(const Data& ob)
{
a = ob.a;
}
}
//使用旧对象初始化新对象调用拷贝构造
{
Data ob1(100);
//拷贝构造
Data ob2(ob1);
}
3.2 普通对象作为函数参数传入,调用拷贝构造
3.3 普通函数作为函数的返回值
函数的返回值如果小于四个字节,存放于寄存器中,如果大于四个字节,则在栈区中临时开辟一块空间来存放。
在visual studio 下会发生拷贝构造
在Linux和Qtcreater下不会发生拷贝构造
4.拷贝构造的深拷贝和浅拷贝
默认的拷贝构造都是浅拷贝,也就是直接把旧对象的所有成员变量的数据直接赋值给新的对象。
那么如果成员中有成员指针,就需要进行深拷贝,也就是对新成员的指针指向进行重新设置,防止两个对象的指针成员都指向同一块空间,造成析构的时候空间重复释放。
【初始化列表】
一个类作为另一个类的成员,那么这个类叫做成员对象
如果想要让这个成员对象调用构造函数,那么就需要使用初始化列表
由于成员对象b是先于对象a开辟空间的,所以如果想要调用成员对象b的有参构造就要在对象a构造之前调用,就需要使用初始化列表。
class B
{
public:
int num;
};
class A
{
public:
B b;
int num;
//只有现对初始化列表中的成员赋值后,才会往下走
//B b = B(a) 显式有参构造
A(int a,int b):B(a),num(b)
{
}
};
【对象数组】
对象数组本质是数组,但是数组里面的元素是对象,对象数组主要注意事项就是对象数组的定义
class Data
{
.......
}
//需要使用显式调用或者隐式调用
Data arr[3] = {Data(1),Data(2),Data(3)};
【explicit关键字】
对象的实例化有很多种方式,其中有一种很特别的叫做隐式转换,存在于类中只有一个成员的情况下,具体形式如下
class A
{
public:
int a;
};
实例化对象
A a = 500;
如果类只有一个成员,可以这样赋值,但是很容易造成误会,觉得是对a变量赋值500,所以C++中使用explicit关键字来防止这个情况出现,只需要在构造函数前加explicit关键字修饰即可