类
类声明中的内容包括数据和函数,分别称为数据成员和成员函数。按访问权限划分,数据成员和成员函数又可分为共有、保护和私有3种
class 类名{
public:
公有数据成员;
公有成员函数;
protected:
保护数据成员;
保护成员函数;
private:
私有数据成员;
私有成员函数;
};
这个类和C语言中的结构体很像,这个像不是它里面的数据,而是用法,比如class Area {.......} area1,area2;Area area ;Area *area;
- 类声明中的关键字private、protected、public可以任意顺序出现。
- 若私有部分处于类的第一部分时,关键字private可以省略。这样,如果一个类体中没有一个访问权限关键字,则其中的数据成员和成员函数都默认为私有的。
- 不能在类声明中给数据成员赋初值。
成员函数
普通成员函数的定义
在类的声明中只给出成员函数的原型,而成员函数的定义写在类的外部。这种成员函数在类外定义的一般形式是:
返回值类型 类名::成员函数名(参数表){函数体}
#include <iostream>
using namespace std;
class Area {
public :
int ShowArea();
void SetLongWide (int x, int y);
private:
int lenth;
int wide;
};
void Area::SetLongWide(int x, int y) {
lenth = x;
wide = y;
}
int Area::ShowArea() {
return lenth * wide;
}
int main() {
int i = 0;
Area area;
area.SetLongWide(3,4);
i = area.ShowArea();
cout << "面积:" << i << endl;
return 0;
}
内联函数的定义
在函数名前冠以关键字inline
,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。
- 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
- 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
隐式声明:将成员函数直接定义在类的内部
#include <iostream>
using namespace std;
class Area {
public :
int ShowArea() {
return lenth * wide;
}
void SetLongWide (int x, int y) {
lenth = x;
wide = y;
}
private:
int lenth;
int wide;
};
int main() {
int i = 0;
Area area;
area.SetLongWide(3,4);
i = area.ShowArea();
cout << "面积:" << i << endl;
return 0;
}
显式声明:在类声明中只给出成员函数的原型,而将成员函数的定义放在类的外部。
#include <iostream>
using namespace std;
class Area {
public :
int ShowArea();
void SetLongWide (int x, int y);
private:
int lenth;
int wide;
};
inline void Area::SetLongWide(int x, int y) {
lenth = x;
wide = y;
}
inline int Area::ShowArea() {
return lenth * wide;
}
int main() {
int i = 0;
Area area;
area.SetLongWide(3,4);
i = area.ShowArea();
cout << "面积:" << i << endl;
return 0;
}
在类中,使用inline定义内联函数时,必须将类的声明和内联成员函数的定义都放在同一个文件(或同一个头文件)中,否则编译时无法进行代码置换。
构造函数
构造函数是一种特殊的成员函数,它主要用于为对象分配空间,进行初始化。构造函数的名字必须与类名相同,而不能由用户任意命名。它可以有任意类型的参数,但不能具有返回值。它不需要用户来调用,而是在建立对象时自动执行。
#include <iostream>
using namespace std;
class Area {
public:
Area(int m, int f);
int ShowArea();
void SetLongWide(int x, int y);
private:
int lenth;
int wide;
};
Area::Area(int m, int f)
{
lenth = m;
wide = f;
}
//或者如下:
//Area::Area(int m, int f):lenth(m), wide(f) {
// cout << "构造函数使用中..." << endl;
//}
void Area::SetLongWide(int x, int y) {
lenth = x;
wide = y;
}
int Area::ShowArea() {
return lenth * wide;
}
int main() {
int i = 0;
Area area(1,2);
area.SetLongWide(3, 4);
i = area.ShowArea();
cout << "面积:" << i << endl;
return 0;
}
或者采用如下的方式:
#include <iostream>
using namespace std;
class Area {
public:
Area(int m, int f) :lenth(m), wide(f) {}
int ShowArea();
void SetLongWide(int x, int y);
private:
int lenth;
int wide;
};
void Area::SetLongWide(int x, int y) {
lenth = x;
wide = y;
}
int Area::ShowArea() {
return lenth * wide;
}
int main() {
int i = 0;
Area area(1,2);
//area.SetLongWide(3, 4);
i = area.ShowArea();
cout << "面积:" << i << endl;
return 0;
}
- 构造函数的名字必须与类名相同,否则编译程序将把它当做一般的成员函数来处理。
- 构造函数没有返回值,在定义构造函数时,是不能说明它的类型的。
- 与普通的成员函数一样,构造函数的函数体可以写在类体内,也可写在类体外。
- 构造函数一般声明为共有成员,但它不需要也不能像其他成员函数那样被显式地调用,它是在定义对象的同时被自动调用,而且只执行一次。
- 构造函数可以不带参数。
析构函数
析构函数也是一种特殊的成员函数。它执行与构造函数相反的操作,通常用于撤销对象时的一些清理任务,如释放分配给对象的内存空间等。析构函数有以下一些特点:
析构函数与构造函数名字相同,但它前面必须加一个波浪号(~)。
析构函数没有参数和返回值,也不能被重载,因此只有一个。
当撤销对象时,编译系统会自动调用析构函数。
如果没有给类定义构造函数和析构函数,则编译系统自动生成一个默认的构造函数和析构函数。
带有默认参数值的函数
- 当进行函数调用时,编译器按从左到右的顺序将实参与形参结合,若未指定足够的实参,则编译器按顺序用函数原型中的默认值来补足所缺少的实参。
- 在函数原型中,所有取默认值的参数都必须出现在不取默认值的参数的右边。
int fun(int a, int b, int c = 111);
-
在函数调用时,若某个参数省略,则其后的参数皆应省略而采取默认值。不允许某个参数省略后,再给其后的参数指定参数值。
函数重载
在C++中,用户可以重载函数。这意味着,在同一作用域内,只要函数参数的类型不同,或者参数的个数不同,或者二者兼而有之,两个或者两个以上的函数可以使用相同的函数名。
调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。
int mul(int x, int y);
double mul(int x, int y);
函数的重载与带默认值的函数一起使用时,有可能引起二义性。
void Drawcircle(int r = 0, int x = 0, int y = 0);
void Drawcircle(int r);
Drawcircle(20);
在调用函数时,如果给出的实参和形参类型不相符,C++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。
void f_a(int x);
void f_a(long x);
f_a(20.83);
其他基础知识
作用标识符‘: :’
如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上“: :”,此时: :value代表全局变量value,
new和delete运算符
C语言中使用函数malloc()
和free()
来进行动态内存管理。C++则提供了运算符new
和delete
来做同样的工作,而且后者比前者性能更优越,使用更灵活方便。
指针变量名 = new 类型
int *p;
p = new int;
delete 指针变量名
delete p;
下面对new和delete的使用再做一下几点说明:
用运算符new分配的空间,使用结束后应该用也只能用delete显式地释放,否则这部分空间将不能回收而变成死空间。
在使用运算符new动态分配内存时,如果没有足够的内存满足分配要求,new将返回空指针(NULL)。
使用运算符new可以为数组动态分配内存空间,这时需要在类型后面加上数组大小。
指针变量名 = new 类型名[下标表达式];
int *p = new int[10];
释放动态分配的数组存储区时,可使用delete运算符。
delete []指针变量名;
delete p;
new 可在为简单变量分配空间的同时,进行初始化
指针变量名 = new 类型名(初值);
int *p;
p = new int(99);
···
delete p;
引用(reference
)
引用是C++对C的一个重要扩充。变量的引用就是变量的别名,因此引用又称别名。
类型 &引用名 = 已定义的变量名
引用与其所代表的变量共享同一内存单元,系统并不为引用另外分配存储空间。实际上,编译系统使引用和其代表的变量具有相同的地址。
- 不允许建立void类型的引用
- 不能建立引用的数组
- 不能建立引用的引用。不能建立指向引用的指针。引用本身不是一种数据类型,所以没有引用的引用,也没有引用的指针。
- 可以将引用的地址赋值给一个指针,此时指针指向的是原来的变量。
- 可以用const对引用加以限定,不允许改变该引用的值,但是它不阻止引用所代表的变量的值。