前言
这一节主要介绍C++类的知识,关于C++的基础知识(例如c到c++的发展,c++数据类型,位运算之类的吧)准备另写一篇博客介绍。
一、C++类
类实际上是一种模板,不占用内存空间,甚至可以理解为int、double这样的数据类型(这么说是不对的,可以这么理解),基于类创建的对象(这个过程称为实例化),是类类型的一个实例,对象拥有类的所有成员,实例化的对象占用内存。
这里简单介绍一下类包括的不同成员:
- 成员变量
- 成员函数
- 静态成员变量
- 静态成员函数
- const成员变量
- const成员函数(常成员函数)
- this指针
成员变量被称为属性,成员函数被称为方法,对于普通的成员变量,编译后的类不会为其分配内存,所以不能初始化,只有创建对象时才能为其分配内存并按你的需求给变量赋值。
这是一个简单的类定义模板L,记得在类定义结束部分加;
:
class A{
public:
int 属性;//成员变量
void 方法();//成员函数
};
创建对象:
class A tmp;//加了class关键字
A tmp;//没加class关键字,一般使用这种方式进行实例化
A tmp[10]; //对象数组
访问类的成员,这里主要有是对象和对象指针的差别:
对象用
.
访问,对象指针用->
访问;声明的对象存在于栈(stack)中,通过new创建的对象存在于堆(heap)中,栈的内存在模块结束时可以自动释放,堆需要手动通过delete
关键字手动释放,如果没有手动释放,将在整个程序结束时由操作系统自动回收。在堆中创建的对象没有名字,只有一个对外的指针,所以需要一个指针变量接收这个指针。
参考:内存四区:代码区、全局区、栈区、堆区
//对象
A tmp;
tmp.属性=1;
tmp.方法();
//对象指针
A *tmp=new A();
tmp->属性=1;
tmp->方法();
delete tmp;
二、C++ 类的构造函数
类通过一个或几个特殊的成员函数来控制其对象初始化的过程,这些函数称为构造函数(constructor)。需要注意的几点:
- 构造函数不能被声明成
const
; const对象
在构造过程中可以向const成员变量
写值,构造完成后获得“常量”属性。
类具有默认的构造函数,它的初始化规则如下:
- 如果存在初始值,以初始值初始化成员;
- 否则默认初始化改成员。
因为默认构造函数只能初始化简单的类,所以一般我们使用类时,需要自己的构造函数。
一旦定义了其他的构造函数,除非再定义一个构造函数,否则该类没有默认构造函数;
默认构造函数可能执行错误的操作,例如,类如果包含内置类型或复合类型(数组和指针),初始化时值将是未定义的;
编译器不能为某些类生成默认的构造函数,例如类包含其他类类型的成员且这个成员的类型没有默认的构造函数。
也可以使用初始化列表对变量进行初始化,常成员变量必须使用初始化列表进行初始化,该方法的初始化顺序是成员变量的定义顺序
。构造函数也可以在类外定义。
struct A{
private:
char color;
public:
A(char &c){
this.color=c;
}
//构造函数初始值列表
A(char &c):color(c){}
//添加默认构造函数
A()=default;
};
A::A()=default;//错误的,只能声明在类内部
C++11增加了委托构造函数,我在这里就不多说了,感谢的同学可以参考C++ Primer这本书。
class Sales_data{
public:
Sales_data(std::string s, unsigned cnt, double price):
bookNo(s),units_sold(cnt),revenue(cnt*price){}
//委托构造函数
Sales_data():Sales_data("",0,0){}
};
三、C++ 类的析构函数
**析构函数(destructor)**在对象销毁时自动调用,写法在构造函数前面加上符号~
。一个类只能有一个析构函数,而且析构函数不能带有参数,如果没有定义,编译器会自动生成一个析构函数。析构函数的执行与C++的内存管理有关:栈的内存会在代码运行完释放;全局区的内存会在程序结束时释放;而堆中的对象如果不手动释放,就不会调用析构函数,最终内存被操作系统回收。
//析构函数的执行顺序
#include <iostream>
#include <string>
using namespace std;
class A{
private:
string s;
public:
A(string s):s(s){}
~A(){ cout<<s<<endl;}
};
void func(){
//局部对象
A a1("1");
}
//全局对象
A a2("2");
int main(){
//局部对象
A a3("3");
//new创建的对象
A *a4 = new A("4");
func();//代码块执行完,执行释放
cout<<"测试执行顺序"<<endl;//执行输出
return 0;//main函数执行完,释放对象a3
}//释放全局对象a2
运行结果:
四、C++ 类的成员函数
类的成员变量和普通变量没什么大的区别,这里不在赘述,这节主要介绍类的成员函数。首先类的成员函数与普通函数的主要区别在于在何处定义:
- 类体内部定义的函数默认是内联函数,无需添加
inline
关键字; - 类体外定义成员函数需要添加
::作用域运算符
指明函数的出处,当然也可以添加inline
关键字把他定义为内联函数。
// 类内定义成员函数
class A{
public:
int 属性;//成员变量
//成员函数
void 方法(){
cout<<"类内定义成员函数,会自动成为内联函数!"<<endl;
};
};
// 类外定义成员函数
class B{
public:
int 属性;//成员变量
//成员函数
void 方法();
};
void B::方法(){
cout<<"类外定义成员函数"<<endl;
}
inline void B::方法(){
cout<<"类外定义成员函数,添加inline关键字!"<<endl;
}
因为内联函数编译时将函数调用处用函数体替换,所以一般将比较短小、经常使用的函数声明为内联函数,要不然编译后的代码会非常的大。不过
inline
关键字只是程序员对编译器的建议,编译器会自己判断是否把函数作为内联函数编译,不是加了inline
关键字该函数就一定会被作为内联函数编译,嘿嘿,狡猾的编程器。
五、类的访问控制
类的基本思想是数据抽象和封装,访问说明符
加强了类的封装性,访问说明符
分为三类
private
,访问权限限定于类的内部,只能被类内部的成员函数访问,不能被类的实例化对象和派生类访问;public
,在整个程序都可以访问,一般用于定义类的接口;protected
,访问权限限定于类的内部,只能被类内部的成员函数访问,不能被类的实例化对象访问,派生类可以访问。
这里再简要介绍一下声明类的关键字的区别:
class
,默认访问权限是private
struct
,默认访问权限是public
除此之外需要指出的地方是,
struct
默认public
继承,class
默认private
继承,继承方式取决于子类而不是基类。同时struct
是可以使用模板的,但是struct
不能用做模板参数,而class可以用作模板参数。
参考:C++ 结构体(struct)的继承
struct A{};
class B : A{}; //private继承
struct C : B{}; //public继承
template<typename T>
struct A{//正确
private:
T a;
};
template<typename T>//正确
template<class T>//正确
template<struct T>//错误
总结
这些在参考C++ Primer 第五版这本书复习知识,可能会遗漏掉一些东西,大家看到麻烦帮忙指出。