OOP: Object-Oriented Programing (OOP). 面向对象编程
对象:一个对象表示现实世界中一个独一无二的实体
举例:一个学生,一张桌子,一个圆圈,一个按钮,甚至一笔贷款都可以看成是对象
我们将这四个单词的首字母放在一起: A PIE
中文含义是:一块甜饼。 这样记忆就方便多了。
对象是类的实例
可以看成,类是一种由程序员定义的数据类型。
简单的对比,以前数据类型是int
/double
,现在变成了类ClassName
;以前变量可以用int
/double
定义,如int a
,现在<对象>由类进行实例化,如ClassName ObjectName
:
类包含:
类有两种特殊的函数:
实例:
class Circle {
private:
double radius;
public:
// 无参构造函数(默认构造函数)
// [C++11]:default,在有含参构造函数的情况下,同时让编译器生成一个重载的无参(默认)构造函数
Circle() = default;
// 含参构造函数
Circle(double r) {
radius = r;
}
// 设置半径
void setRadius(double r) {
radius = r;
}
// 获取半径
double getRadius() const {
return radius;
}
// 获取面积
double getArea() const {
return 3.14 * radius * radius;
}
};
特点:
类可不声明构造函数:
编译器会提供一个带有空函数体的无参构造函数
但:只有当未明确声明构造函数时,编译器才会提供这个构造函数,并称之为“默认构造函数”
比如,上述代码段中,我们明确了一个含参构造函数Circle(double r);
,那么编译器不会自动生成无参(默认)构造函数,需要程序员自己声明。
即:上述代码,去掉语句Circle() = default;
后,无法通过编译。同时,default关键字是C++11开始有的,该语句等同于Circle() {}
。
如果将上述代码的两个构造函数都删去,可通过编译,表明了<类可不声明构造函数>
创建一个对象,不传递参数。
Circle circle1; // 正确,但不推荐这样写
Circle circle2(); // 错误!C++编译器认为这是一个函数声明
Circle circle3{}; // 正确,推荐写法。这里面明确显示用空初始化列表初始化circle3对象(调用Circle默认构造函数)
创建一个对象,创建的同时传递参数,用于初始化该对象。
Circle circle2{ 5.5 }; // C++11 列表初始化
// 带有窄化检查(narrowing check)
使用点运算符<.
>访问对象中的数据函数。
点运算符<.
>,也称为对象成员访问运算符。
要注意的是,点运算符<.
>只能访问类的公有(public
)成员,将在《封装》一节详解。
C++中,类声明与实现可以分离:
.h
: 类声明,描述类的结构.cpp
: 类实现,描述类方法的实现类的声明语法如下:
// In .h File
class ClassName {
private:
DataType1 DataName1;
DataType2 DataName2;
public:
FunctionType1 FunctionName1(Arguments);
FunctionType2 FunctionName2(Arguments);
}
类的实现语法如下:
// In .cpp File
FunctionType ClassName::FunctionName (Arguments) {
// Function body
}
其中,:: 这个运算符被称为binary scope resolution operator(二元作用域解析运算符),简称“域分隔符”。
Circle.h
文件:
#ifndef CIRCLE_H_
#define CIRCLE_H_
class Circle {
private:
double radius;
public:
// 无参构造函数(默认构造函数)
Circle();
// 含参构造函数
Circle(double r);
// 设置半径
void setRadius(double r);
// 获取半径
double getRadius() const;
// 获取面积
double getArea() const;
};
#endif /* CIRCLE_H_ */
Circle.cpp
文件:
Circle::Circle() = default;
Circle::Circle(double r) {}
void Circle::setRadius(double r) {
radius = r;
}
double Circle::getRadius() const {
return radius;
}
double Circle::getArea() const {
return 3.14 * radius * radius;
}
main.cpp
文件(实例演示,创建一个Circle对象):
#include
#include "Circle.h"
using std::cout;
using std::endl;
int main() {
Circle c;
c.setRadius(1);
cout << c.getArea() << endl;
return 0;
}
编译这个C++程序的命令是:g++ main.cpp Circle.cpp -o main
,或者使用集成开发环境。
当函数在类声明中实现,它自动成为内联函数。
也就是,如果函数体直接写在类的声明种,该函数自动成为内联函数。
实例:
class A {
public:
A() = default; //C++11
double f1() { // f1自动称为内联函数
// do something
}
double f2();
};
double A::f2() { // f2不是内联函数
//do something
}
class A {
public:
A() = default; //C++11
double f1();
double f2();
};
double A::f2() {
//do something
}
inline double A::f1() { // f1是内联函数
//do something
}
在.h
文件中可以使用三种编译预处理指令:
#ifndef MY_HEADER_FILE_H
#define MY_HEADER_FILE_H
// 头文件内容
#endif
#pragma once // C++03, C90
_Pragma("once") // C++11, C99;
// 如果是MSVC编译器,那么是:
__pragma(once);
用类声明一个实体的说法,与定义变量的说法有些不同:用原生数据类型定义变量,用类名定义对象。
// class --> objects
Circle c1; //调用Circle的默认ctor
Circle c2(5.5); //调用Circle的有参ctor,不推荐这种语法
Circle c3{5.5}; // 直接列表初始化,调有参ctor
Circle c4 = {5.5}; // 拷贝列表初始化,调ctor
auto c5 = Circle{2.}; // auto类型推断
decltype(c1) c6; // decltype类型推断
如何将一个对象的内容拷贝给另外一个对象?
使用赋值运算符<=
>
默认情况下,对象中的每个数据域都被拷贝到另一对象的对应部分
实例:
Circle c1{2.0};
Circle c2;
c2 = c1
有时需要创建一个只用一次的对象,这种不命名的对象叫做匿名对象( Anonymous Object)。
实例:
int main() {
Circle c1 = Circle{1.1};
auto c2 = Circle{2.2}; // 用匿名对象做拷贝列表初始化
Circle c3{}; // 直接列表初始化,调默认Ctor
c3 = Circle{3.3}; // 用匿名对象赋值
cout << "Area is " << Circle{4.2}.getArea() << endl;
cout << "Area is " << Circle().getArea() << endl; // 不推荐的语法
cout << "Area is " << Circle(5).getArea() << endl; // 不推荐的语法
return 0;
}
注意到,上述代码第2、3行,使用了匿名对象对c1、c2做初始化。而第5行,使用了匿名对象对c3进行了成员拷贝(也可以叫做对c3对象进行了赋值操作)。
C语言中使用结构体类型来表示一组数据。
在C++中,结构体已被类取代。
实例:
void f(){
class C { // C及其对象只在f()中可用
void g() { // 成员函数必须在C中实现
/* 访问f()的成员受限 ……. */
}
};
C c1, c2;
}
嵌套类是在另一个类中声明的类。
实例:
class E{
class N { // N及其对象可访问E的成员
/* 声明N的成员 ……. */
}
};
N n1, n2;
}