将一组对象的共同特征抽象出来, 从而造成类的概念.程序员
类包括数据成员和成员函数, 不能在类的声明中对数据成员进行初始化数组
形式为:bash
class 类名 {
private:
私有数据和函数
public:
公有数据和函数
protected:
受保护的数据和函数
}; // 注意分号
复制代码
不管是数据成员仍是成员函数, 都是这个类的成员, 都具备一个访问权限, 若是没有关键字进行修饰, 则默认为private权限函数
声明一个类, 像这样:学习
// 声明类
class Point {
// 若是没有修饰符, 默认为私有的权限
double x;
double y;
public:
// 无参构造函数
Point();
// 有参构造函数
Point(double a, double b);
// 成员函数
void display();
};复制代码
形式为:测试
// :: 为做用域运算符, 表示这个函数属于哪一个类
返回类型 类名::成员函数名(参数列表) {
函数体 // 内部实现
}复制代码
咱们在上面的声明类的代码中, 声明了成员函数, 咱们能够在类外面定义成员函数, 也就是给出函数体优化
像这样:ui
// 定义成员函数
// 无参构造函数
Point::Point() {}
// 有参构造函数
Point::Point(double a, double b) {
x = a;
y = b;
}
// 可使用关键字inline将成员函数定义为内联函数
inline void Point::display() {
cout << x << ", " << y << endl;
}
复制代码
若是在声明类的同时, 在类体内给出成员函数的定义, 则默认为内联函数this
咱们通常都是在类体内存给出成员函数的定义spa
像这样, 完成一个类的声明和定义
// 声明类
class Point {
double x;
double y;
public:
// 定义成员函数
Point() {}
Point(double a, double b) {
x = a;
y = b;
}
void display() { // 默认为内联函数
cout << x << ", " << y << endl;
}
}; 复制代码
像这样是不行的:
class Point {
// 在类体内不能给数据成员赋值
double x = 2;
double y = 3;
}
// 在类体外不能给数据成员赋值
// x = 4;
// y = 5;复制代码
只有产生了具体对象, 这些数据值才有意义
初始化: 在产生对象时就使对象的数据成员具备指定值, 则称为对象的初始化
赋值: 有了对象以后, 对象调用本身的成员函数实现赋值操做
类的成员函数能够直接使用本身类的私有成员
类外面的函数不能直接访问类的私有成员, 而只能经过类的对象使用公有成员函数
定义类对象指针的语法: 类名 * 对象指针名 = 对象地址;
经过对象指针能够访问对象的成员: 对象指针名 -> 对象成员名;
像这样:
// 定义一个类
class Point {
// 声明数据成员
double x;
double y;
public:
// 声明而且定义成员函数
void setXY(double a, double b) {
x = a;
y = b;
}
// 一个类的成员函数能够访问本身的私有成员
void display() {
cout << x << ", " << y << endl;
}
};
int main() {
// 定义对象a
Point a;
// 定义b为对象a的引用
Point & b = a;
// 定义p为指向对象a的指针
Point *p = &a;
// 对象和引用, 都使用"."访问对象的成员
a.setXY(2, 3);
b.setXY(4, 5);
// 指针使用"->"访问对象的成员
p -> setXY(6, 7);
}复制代码
一个类若是没有定义任何构造函数, 编译器会自动定义一个不带参数的构造函数, 也就是默认构造函数
好比咱们有一个类Point
则默认构造函数就是这样:
Point::Point() {};复制代码
若是一个类提供了构造函数, 系统再也不提供默认构造函数
咱们有一个Point类, 像这样:
class Point {
double x;
double y;
public:
Point(double a, double b) {
x = a;
y = b;
}
};复制代码
则咱们就不能在main函数中这样使用:
int main() {
// Point类有本身定义的构造函数Point(double, double), 因此就没有默认构造函数Point()
// 这句话调用的是无参构造函数来定义一个对象, 因此编译错误
// 咱们须要给类Point加上无参构造函数
Point a;
}复制代码
咱们想要这样使用, 则必须手动添加无参数构造函数
像这样:
class Point {
double x;
double y;
public:
// 无参构造函数
Point(){}
// 有参构造函数
Point(double a, double b) {
x = a;
y = b;
}
;
int main() {
// 咱们给类加上自定义无参构造函数, 如今正确编译
Point a;
}复制代码
构造函数的名字应该与类名同名, 并在定义构造函数时不能指定返回类型, void也不能够
// 声明一个类
class Point {
double x;
double y;
public:
// 声明无参构造函数
Point();
// 声明有参构造函数
Point(double, double);
};
// 在类外定义构造函数
Point::Point(){}
// 第一种定义构造函数
//Point::Point(double a, double b):x(a),y(b){}
// 第二种定义构造函数
Point::Point(double a, double b) {
x = a;
y = b;
}
int main() {
// 产生对象
Point a(2, 3);
}
复制代码
咱们通常都在类的声明内部进行函数定义
像这样:
// 定义一个类
class Point {
// 声明数据成员
double x;
double y;
public:
// 无参构造函数
Point(){};
// 有参构造函数
Point(double a, double b) {
x = a;
y = b;
}
};
int main() {
// 无参构造函数 产生对象a
Point a;
// 有参构造函数 产生对象b
Point b(2, 3);
// 无参构造函数 产生对象数组arr1
Point arr1[2];
// 有参构造函数 产生对象数组arr2
Point arr2[2] = {Point(2, 3), Point(4, 5)};
}复制代码
不能在程序中显式地调用构造函数, 构造函数是自动调用的
即不能这样: Point a.Point(2, 3);
只能这样: Point a(2, 3);
用来在产生对象的同时, 进行对象的初始化
new用来创建生存期可控的动态对象, 返回这个对象的指针
new和构造函数一同起做用
过程: 当用new创建动态对象时, 首先分配能够保存这个类对象的内存空间, 而后自动调用构造函数来初始化这块内存, 再返回这个动态对象的地址
使用new创建的动态对象只能使用delete删除, 以释放所占空间
像这样:
// new与无参构造函数
Point * p1 = new Point;
// new与有参构造函数
Point * p2 = new Point(2, 3);
复制代码
若是咱们定义了有参构造函数, 又想使用无参构造函数, 咱们能够将有参构造函数的参数所有使用默认参数
像这样:
class Point {
double x;
double y;
public:
// 声明构造函数
// 有参构造函数
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成员函数
void display() {
cout << x << ", " << y << endl;
}
};
int main() {
// 产生对象
Point a;
a.display();
}复制代码
做用: 经过拷贝方式使用一个类的已有对象来创建一个该类的新对象, 通常编译器会创建一个默认的复制构造函数
像这样:
类名(const 类名 &); // 为了避免改变原有对象, 使用const来进行修饰
复制代码
复制构造函数也能够自定义, 则编译器再也不调用默认的复制构造函数
像这样:
// 定义一个类
class Point {
// 声明数据成员
double x;
double y;
public:
// 无参构造函数
Point(){
cout << "默认构造函数" << endl;
};
// 有参构造函数
Point(double a, double b) {
x = a;
y = b;
cout << "构造函数 " << x << ", " << y << endl;
}
// 复制构造函数
Point(const Point & t) {
x = t.x;
y = t.y;
cout << "复制构造函数" << endl;
}
};
int main() {
// 使用默认构造函数产生一个对象
Point a;
// 使用复制构造函数产生一个对象
Point b(a);
}复制代码
使用复制构造函数的三种状况
像这样:
// 经过构造函数实例化对象
Point a(2, 3);
// 经过构造函数实例化对象
Point b(a);
// 调用成员函数
b.display();
复制代码
像这样:
// 函数, 用来显示一个Point对象
void printPoint(Point t) { // 当函数调用时形参t是经过复制构造函数来产生的对象
t.display();
} // 函数执行完毕后, 调用形参t的析构函数, 释放内存
int main() {
// 产生对象a
Point a(2, 3);
// 调用函数
printPoint(a);
} // 函数执行完毕后, 调用对象a的析构函数, 释放内存
复制代码
// 函数, 获得一个Point对象
Point getPoint() {
Point * t = new Point(2, 3);
return *t; // 产生一个对象
} // 函数执行完毕后, 调用对象t的析构函数, 释放内存
int main() {
// 调用函数返回一个类对象, 这里在接收函数返回的对象时会自动调用复制构造函数(不调用的是编译器进行了优化)
Point a = getPoint();
} // 函数执行完毕后, 调用对象a的析构函数, 释放内存
复制代码
函数参数使用对象的引用不产生副本, 因此当对象做为函数参数时, 推荐使用对象引用这种方式
在对象消失时, 使用析构函数释放由构造函数分配的内存
为了与构造函数区分, 在析构函数前加”~”号,
而且在定义析构函数时, 不能指定返回类型, 即便是void类型也不能够;
也不能指定参数, 但能够显式的说明参数为void
格式: ~类名(); // 或者 ~类名(void);
复制代码
代码像这样:
~Point(); // 或者 ~Point(void);复制代码
析构函数在对象的生存期结束时自动调用, 而后对象占用的内存被回收
全局对象和静态对象的析构函数在程序运行结束以前调用
类对象的数组每一个元素调用一次析构函数
像这样: 能够运行该代码, 查看程序执行过程
// 定义一个类
class Point {
// 声明数据成员
double x;
double y;
public:
// 无参构造函数
Point(){
cout << "默认构造函数" << endl;
};
// 有参构造函数
Point(double a, double b) {
x = a;
y = b;
cout << "构造函数 " << x << ", " << y << endl;
}
// 复制构造函数
Point(const Point & t) {
x = t.x;
y = t.y;
cout << "复制构造函数" << endl;
}
// 析构函数
~ Point() {
cout << "析构函数" << endl;
}
};
int main() {
// 使用默认构造函数产生一个对象
Point a; // 调用默认构造函数, 产生新对象
// 使用复制构造函数产生一个对象
Point b(a); // 调用复制构造函数, 产生新对象
// 对象数组
Point arr[2]; // 调用默认构造函数 2次, 产生两个新对象
} // 程序结束后, 由于总共产生了4个对象, 因此也会调用4次析构函数复制代码
当使用运算符delete删除一个动态对象时, 首先为这个对象调用析构函数, 而后再释放这个动态对象占用的内存
像这样:
// 使用new和默认构造函数产生一个对象
Point * p = new Point;
// 使用delete来释放内存
delete p;
// 使用new和默认构造函数产生一个对象数组, 数组有两个对象
Point * p2 = new Point[2];
// 使用delete释放数组内存
delete []p2;复制代码
若是没有定义析构函数, 编译器自动为类产生一个函数体为空的默认析构函数
像这样:
~ Point(){};复制代码
成员函数可重载或使用默认参数, 为了提升可读性
// 定义一个类
class MyMax {
// 私有成员
int a, b, c, d;
// 函数: 求两个数的最大值
int getMax(int v1, int v2) {
return v1 > v2 ? v1 : v2;
}
// 公有成员
public:
// 函数: 改变数据成员的值
void setValue(int v1, int v2, int v3 = 0, int v4 = 0) {
a = v1;
b = v2;
c = v3;
d = v4;
}
// 函数: 得到全部数据成员里的最大值 (函数重载)
int getMax() {
return getMax(getMax(a, b), getMax(c, d));
}
};
int main() {
// 产生对象
MyMax a;
// 改变数据成员的值
a.setValue(1, 2, 3, 4);
// 调用成员函数
cout << a.getMax() << endl;
}
复制代码
当一个成员函数被调用时, 系统自动向该函数传递一个隐含的参数, 指向调用该函数的对象指针, 名为this, 从而使用成员函数知道该对哪一个对象进行操做.
做用: 它将对象和该对象调用的成员函数链接在一块儿, 从外部看来, 每一个对象都拥有本身的成员函数, 但处理这些数据成员的代码能够被全部的对象共享
咱们通常状况下都会省略this
// 定义一个类
class Point {
double x;
double y;
public:
// 有参构造函数
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成员函数
void display() {
cout << x << ", " << y << endl;
}
// 伪代码
// void setXY(double x, double y Point * this) {
// this -> x = x;
// this -> y = y;
// }
// 咱们能够写成这样
// void setXY(double x, double y) {
// this -> x = x;
// this -> y = y;
// }
// 可是通常咱们都写成这样
void setXY(double x, double y) {
x = x;
y = y;
}
};
int main() {
// 经过构造函数实例化对象
Point a(2, 3);
// 调用成员函数
a.display();
}
复制代码
由于类自己就是一种新的数据类型, 因此一个类的对象能够做为另外一个类的成员
像这样:
// 类: Point, 包含两个数据成员
class Point {
double x;
double y;
public:
// 有参构造函数
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成员函数
void display() {
cout << x << ", " << y << endl;
}
};
// 类: Line, 包含两个Point对象
class Line {
// 两个为Point类的对象做为数据成员
Point startPoint;
Point endPoint;
public:
// 构造函数
Line(Point start, Point end) {
startPoint = start;
endPoint = end;
}
// 成员函数
void display() {
cout << "起点: ";
startPoint.display();
cout << "终点: ";
endPoint.display();
}
// 返回一个Point对象: 起点
Point getStartPoint() {
return startPoint;
}
// 返回一个Point对象: 终点
Point getEndPoint() {
return endPoint;
}
};
int main() {
// 经过构造函数实例化对象
Point a(2, 3);
Point b(4, 5);
// 经过已有对象实例化另外一个对象
Line lineA(a, b);
// 调用成员函数
lineA.display();
// 调用一个对象的成员函数, 返回另外一个对象
Point startPoint = lineA.getStartPoint();
startPoint.display();
}
复制代码
Point a(2, 3); Point b = a;复制代码
Point arr[3];复制代码
Point p = &a;
p -> display();复制代码
void print(Point a) {} // 对象做为函数参数
void print(Point & a) {} // 对象引用做为函数参数 (推荐使用这一种)
void print(Point * p) {} // 对象指针做为函数参数
复制代码
class Point {}
class Line {
Point startPoint;
Point endPoint;
}
复制代码
类自己的成员函数可使用类的全部成员(私有和公有和受保护的成员)
类的对象只能访问公有成员函数
其它函数不能使用类的私有成员, 也不能使用公有成员函数
虽然一个类能够包含另外一个类的对象, 但这个类也只能经过被包含的类对象使用成员函数, 再访问数据成员
class People; // 不彻底的类声明
People * p; // 定义一个全局变量类指针
复制代码
只有使用类产生对象时, 才进行内存分配
不彻底类不能进行实例化, 不然编译出错, 咱们使用得不是不少
class Empty {};
复制代码
能够不包括任何声明, 也能够没有任何行为, 但能够产生空类对象
像这样:
// 定义一个空类
class Empty {
public:
Empty(){};
};
int main() {
// 产生空类对象
Empty e;
}
复制代码
做用: 在开发大型项目时, 须要在一些类尚未彻底定义或实现时进行先期测试, 保证代码能正确地被编译, 固然咱们有时也会给它一个无参构造函数, 来消除警告
声明类时使用的一对花括号{}造成类的做用域, 也包括类体外成员函数的做用域.
在类做用域中声明的标识符只在类中可见.
像这样:
// 定义类
class Example {
int number;
public:
void setValue(int value) {
// number在类做用域在内部因此有效, 可使用
number = value;
}
void changValue(int);
};
void Example::changValue(<#int#> value) {
// 类的做用域也包括成员函数做用域, number有效
number = value;
}
//int a = number; // 错误, 由于这是在类做用域的外部, number无效
int number; // 正确, 这代码定义了一个全局变量number复制代码
每一个语言的类和对象其实大同小异, 可能一些名字不同, 可能一些格式不同, 可是思想是同样的, 例如一个对象的产生, 都得申请内存, 而后再对这块内存进行初始化, 有本身的属性, 还有本身的行为. 咱们在学习的时候不要纠结于语言的自己, 要学会总结和本身已经学过的其它语言的异同点, 从而总结出规律, 提炼出本质, 这才是最主要的. 今天看到一段话送给你们, 大概是这么说的: 不是咱们变老了就当不了程序员了, 而是由于咱们不想学习了, 因此才显得咱们变老了, 因此也就当不了程序员了!