本篇要学习的内容和知识结构概览

类及其实例化

类的定义

将一组对象的共同特征抽象出来, 从而形成类的概念.

类包括数据成员和成员函数, 不能在类的声明中对数据成员进行初始化

声明类

形式为:

class 类名 {
private:
私有数据和函数
public:
公有数据和函数
protected:
受保护的数据和函数
}; // 注意分号

无论是数据成员还是成员函数, 都是这个类的成员, 都具有一个访问权限, 如果没有关键字进行修饰, 则默认为private权限

声明一个类, 像这样:

// 声明类
class Point {
// 如果没有修饰符, 默认为私有的权限
double x;
double y; public:
// 无参构造函数
Point(); // 有参构造函数
Point(double a, double b); // 成员函数
void display();
};

定义成员函数

形式为:

// :: 为作用域运算符, 表示这个函数属于哪个类

返回类型 类名::成员函数名(参数列表) {

函数体 // 内部实现

}

我们在上面的声明类的代码中, 声明了成员函数, 我们可以在类外面定义成员函数, 也就是给出函数体

像这样:

// 定义成员函数
// 无参构造函数
Point::Point() {} // 有参构造函数
Point::Point(double a, double b) {
x = a;
y = b;
} // 可以使用关键字inline将成员函数定义为内联函数
inline void Point::display() {
cout << x << ", " << y << endl;
}

如果在声明类的同时, 在类体内给出成员函数的定义, 则默认为内联函数

我们一般都是在类体内存给出成员函数的定义

像这样, 完成一个类的声明和定义

// 声明类
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 = ;
double y = ;
} // 在类体外不能给数据成员赋值
// 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(, );
b.setXY(, ); // 指针使用"->"访问对象的成员
p -> setXY(, );
}

构造函数

默认构造函数

一个类如果没有定义任何构造函数, 编译器会自动定义一个不带参数的构造函数, 也就是默认构造函数

比如我们有一个类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(, );
}

我们一般都在类的声明内部进行函数定义

像这样:

// 定义一个类
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(, ); // 无参构造函数 产生对象数组arr1
Point arr1[]; // 有参构造函数 产生对象数组arr2
Point arr2[] = {Point(, ), Point(, )};
}

注意

不能在程序中显式地调用构造函数, 构造函数是自动调用的

即不能这样: Point a.Point(2, 3);

只能这样: Point a(2, 3);

作用

用来在产生对象的同时, 进行对象的初始化

构造函数和运算符new

new用来建立生存期可控的动态对象, 返回这个对象的指针

new和构造函数一同起作用

过程: 当用new建立动态对象时, 首先分配可以保存这个类对象的内存空间, 然后自动调用构造函数来初始化这块内存, 再返回这个动态对象的地址

使用new建立的动态对象只能使用delete删除, 以释放所占空间

像这样:

// new与无参构造函数
Point * p1 = new Point; // new与有参构造函数
Point * p2 = new Point(, );

构造函数的默认参数

如果我们定义了有参构造函数, 又想使用无参构造函数, 我们可以将有参构造函数的参数全部使用默认参数

像这样:

class Point {
double x;
double y; public:
// 声明构造函数
// 有参构造函数
Point(double a = , double b = ) {
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(, ); // 通过构造函数实例化对象
Point b(a); // 调用成员函数
b.display();

如果函数的形参是类的对象, 调用函数时, 进行形参与实参的结合时, 需要调用复制构造函数

像这样:

// 函数, 用来显示一个Point对象
void printPoint(Point t) { // 当函数调用时形参t是通过复制构造函数来产生的对象
t.display();
} // 函数执行完毕后, 调用形参t的析构函数, 释放内存 int main() {
// 产生对象a
Point a(, ); // 调用函数
printPoint(a);
} // 函数执行完毕后, 调用对象a的析构函数, 释放内存

如果函数的返回值是对象, 当函数调用完成返回时, 需要调用复制构造函数, 产生临时对象, 并在执行完返回值语句后, 析构临时对象

// 函数, 得到一个Point对象
Point getPoint() {
Point * t = new Point(, );
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次, 产生两个新对象
} // 程序结束后, 因为总共产生了4个对象, 所以也会调用4次析构函数

析构函数和运算符delete

当使用运算符delete删除一个动态对象时, 首先为这个对象调用析构函数, 然后再释放这个动态对象占用的内存

像这样:

// 使用new和默认构造函数产生一个对象
Point * p = new Point; // 使用delete来释放内存
delete p; // 使用new和默认构造函数产生一个对象数组, 数组有两个对象
Point * p2 = new Point[]; // 使用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 = , int v4 = ) {
a = v1;
b = v2;
c = v3;
d = v4;
} // 函数: 获得所有数据成员里的最大值 (函数重载)
int getMax() {
return getMax(getMax(a, b), getMax(c, d));
}
}; int main() {
// 产生对象
MyMax a; // 改变数据成员的值
a.setValue(, , , ); // 调用成员函数
cout << a.getMax() << endl;
}

this指针

this指针的概念和作用

当一个成员函数被调用时, 系统自动向该函数传递一个隐含的参数, 指向调用该函数的对象指针, 名为this, 从而使用成员函数知道该对哪个对象进行操作.

作用: 它将对象和该对象调用的成员函数连接在一起, 从外部看来, 每个对象都拥有自己的成员函数, 但处理这些数据成员的代码可以被所有的对象共享

this指针的实际形式

我们一般情况下都会省略this

// 定义一个类
class Point {
double x;
double y; public:
// 有参构造函数
Point(double a = , double b = ) {
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(, ); // 调用成员函数
a.display();
}

一个类的对象作为另一个类的成员

因为类本身就是一种新的数据类型, 所以一个类的对象可以作为另一个类的成员

像这样:

// 类: Point, 包含两个数据成员
class Point {
double x;
double y; public:
// 有参构造函数
Point(double a = , double b = ) {
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(, );
Point b(, ); // 通过已有对象实例化另一个对象
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();复制代码

对象作为函数参数时, 可以使用对象, 对象引用和对象指针三种方式, 推荐使用对象的引用作为函数参数, 可以使用const修饰符保证原来的对象不被修改

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

总结

每个语言的类和对象其实大同小异, 可能一些名字不一样, 可能一些格式不一样, 但是思想是一样的, 例如一个对象的产生, 都得申请内存, 然后再对这块内存进行初始化, 有自己的属性, 还有自己的行为. 我们在学习的时候不要纠结于语言的本身, 要学会总结和自己已经学过的其它语言的异同点, 从而总结出规律, 提炼出本质, 这才是最主要的. 今天看到一段话送给大家, 大概是这么说的: 不是我们变老了就当不了程序员了, 而是因为我们不想学习了, 所以才显得我们变老了, 所以也就当不了程序员了!

自学C/C++编程难度很大,不妨和一些志同道合的小伙伴一起学习成长!

C语言C++编程学习交流圈子,【点击进入微信公众号:C语言编程学习基地

有一些源码和资料分享,欢迎转行也学习编程的伙伴,和大家一起交流成长会比自己琢磨更快哦!

C/C++编程笔记:C++入门知识丨类和对象的更多相关文章

  1. C/C++编程笔记:C++入门知识丨从结构到类的演变

    先来看看本节知识的结构图吧! 接下来我们就逐步来看一下所有的知识点: 结构的演化 C++中的类是从结构演变而来的, 所以我们可以称C++为”带类的C”. 结构发生质的演变 C++结构中可以定义函数, ...

  2. C/C++编程笔记:C++入门知识丨继承和派生

    本篇要学习的内容和知识结构概览 继承和派生的概念 派生 通过特殊化已有的类来建立新类的过程, 叫做”类的派生”, 原有的类叫做”基类”, 新建立的类叫做”派生类”. 从类的成员角度看, 派生类自动地将 ...

  3. [Java入门笔记] 面向对象编程基础(一):类和对象

    什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...

  4. 21天学通C++学习笔记(九):类和对象

    1. 类和对象 现实中的人等事物往往具备一些特征并且可以做某些事情,要在程序中模拟这些事物,需要一个结构,将定义其属性(数据)以及其可用这些属性执行的操作(函数)整合在一起.这种结构就是类,而这种结构 ...

  5. C/C++编程笔记:C++入门知识丨运算符重载

    本篇要学习的内容和知识结构概览 运算符重载使用场景 常规赋值操作 我们现在有一个类 想要实现这种赋值操作 具体实现如下: 所以说呢,我们在使用运算符进行运算的时候, 实际上也是通过函数来实现运算的. ...

  6. C/C++编程笔记:C++入门知识丨多态性和虚函数

    本篇要学习的内容和知识结构概览 多态性 编译时的多态性称为静态联编. 当调用重载函数时, 在编译期就确定下来调用哪个函数. 运行时的多态性称为动态联编. 在运行时才能确定调用哪个函数, 由虚函数来支持 ...

  7. C/C++编程笔记:C++入门知识丨函数和函数模板

    本篇要学习的内容和知识结构概览 函数的参数及其传递方式 1. 函数参数传递方式 传值: 传变量值: 将实参内存中的内容拷贝一份给形参, 两者是不同的两块内存 传地址值: 将实参所对应的内存空间的地址值 ...

  8. C/C++编程笔记:C++入门知识丨认识C++面向过程编程的特点

    一. 本篇要学习的内容和知识结构概览 二. 知识点逐条分析 1. 使用函数重载 C++允许为同一个函数定义几个版本, 从而使一个函数名具有多种功能, 这称之为函数重载. 像这样: 虽然函数名一样, 但 ...

  9. C/C++编程笔记:C++入门知识丨认识C++的函数和对象

    一. 本篇要学习的内容和知识结构概览 二. 知识点逐条分析 1. 混合型语言 C++源文件的文件扩展名为.cpp, 也就是c plus plus的简写, 在该文件里有且只能有一个名为main的主函数, ...

随机推荐

  1. css transparent属性_css 透明颜色transparent的使用

    在css中 transparent到底是什么意思呢? transparent 它代表着全透明黑色,即一个类似rgba(0,0,0,0)这样的值. 例如在css属性中定义:background:tran ...

  2. mysql高可用架构MHA搭建(centos7+mysql5.7.28)

    无论是传统行业,还是互联网行业,数据可用性都是至关重要的,虽然现在已经步入大数据时代,nosql比较流行,但是作为数据持久化及事务性的关系型数据库依然是项目首选,比如mysql. 现在几乎所有的公司项 ...

  3. directive 实例讲解

    http://my.oschina.net/ilivebox/blog/289670 gulp-nodemon http://www.zhihu.com/question/32123388?sort= ...

  4. 线性DP之机器分配

    题目大意 自己瞅 (懒得打了) 思路 前面是很简单的线性dp,后面是模拟递归输出方案, 模拟递归可以设ny为机器数机器数,nx表示第nx个公司,tot为总盈利,那么则有\(a[nx][i]+dp[nx ...

  5. python中获取文件路径的几种方式

    # 如果执行文件为E:\aa\bb\aa.py 1.获取当前路径 current_path11 = os.path.abspath(__file__) current_path12 = os.path ...

  6. day38 并发编程(理论)

    目录 一.操作系统发展史 二.多道技术 1 单核实现并发的效果 2 多道技术图解 3 多道技术重点 三.进程理论 1 必备知识点 2 进程调度 3 进程的三状态 4 两对重要概念 四.开启进程的两种方 ...

  7. Web_php_unserialize解题思路

    分析一下 __construct:当使用 new 操作符创建一个类的实例时,构造方法将会自动调用 __destuct:在销毁一个类之前执行执行 __wakeup,unserialize()` 会检查是 ...

  8. (五)学习了解OrchardCore笔记——灵魂中间件ModularTenantContainerMiddleware的第一行②模块的功能部分

    在(三)的时候已经说到模块集合用ForEachAsync的扩展方法分配多个任务,把每个modules的ManifestInfo分析出来的功能加入ConcurrentDictionary.我们先看看这个 ...

  9. bzoj3289Mato的文件管理

    bzoj3289Mato的文件管理 题意: 一共有n份资料,每天随机选一个区间[l,r],Mato按文件从小到大的顺序看编号在此区间内的这些资料.他先把要看的文件按编号顺序依次拷贝出来,再用排序程序给 ...

  10. 量子点/钙钛矿 LED的研究概述

    注:参考文献和文章尚在整理ing... 一 常用术语 1.外量子效率(External quantum efficiency,EQE) 这是LED最重要的参数,它的定义为: 因此,EQE越大,发射到外 ...