面向过程编程
面向过程编程POP(Procedure Oriented Programming),以功能为中心,专注于问题的解决。将整个需求分解为若干步骤,每个步骤定义为一个函数,通过逐步调用函数实现整个需求。
例如,张三丰早上开宝马去上班,面向过程编程可以梳理为如下步骤:
面向过程编程的特点,以函数为最小单位,强调的是功能行为,主要考虑怎么做(算法)。面向过程编程=数据+算法,对于给定数据经过函数处理返回结果(IPO,Input Process Output)。
面向过程的优缺点
优点
符合人类思维,各代码块分工明确,需要实现的功能拆分的清晰明了。
缺点
数据和处理该数据的函数是相互分离的。
当数据结构改变时,所有和该数据相关的函数都要修改,程序的可维护性差。
函数功能太固定,不易于复用,不易于扩展。
面向对象编程
面向对象OOP(Object Oriented Programming),面向对象编程考虑的核心不是如何将需求分解为若干步骤,而是将整个需求里面涉及的事物找出来,将事物的数据抽象为属性,事物的行为抽象为方法,再将属性和方法封装在一起形成类,由这些类相互协作完成需求。
例如,张三丰开宝马车去上班,将张三丰抽象为人类,将宝马车抽象为车类,将人和车的数据抽象为属性,动作抽象为方法。
面向对象编程的基本概念
类,描述了一组具有相同特性(数据)和相同行为(函数)的对象,例如,汽车类,书类。
对象,是现实世界实际存在的事物,是类的一个具体示例。例如,某一辆宝马,某一本书。
属性,类中的特征(数据)称为类的属性(数据成员)。例如,汽车的颜色,车牌号码,书的单价作者。
方法,类中的行为(函数)称为类的方法(成员函数)。例如,汽车有加速方法,刹车方法,转向方法等。
类与对象的关系
类与对象是抽象与具体的关系。
类是创建对象的样板,由这个样板可以创建多个具有相同属性和行为的对象。
类本质上是一种自定义数据类型,一个对象是某个类的实例。一个类的多个对象分别拥有自己的属性值,且相互独立。
成员方访问控制权限
私有访问权限 private
private 修饰的数据成员和成员函数只允许本类成员函数访问,对类的外部不可见。
保护访问权限 protected
protected 修饰的数据成员和成员函数,允许本类和本类的派生类(子类)的成员函数访问,对类外部不可见。
公有访问权限 public
public 修饰的数据成员和成员函数,允许本类和本类的派生类(子类)的成员函数访问,对类的外部可见。
/*
class是定义类的关键字
类名是一个标识符(类名每个单词首字母大写,其他字母小写)
一对花括号表示类的作用域,也叫类体。分号表示类定义结束。
关键字private、protected、public称为访问控制修饰符,描述了类成员的可见性。默认(不写)使用private。
类中的成员函数可以在类中直接写出函数定义,也可以只写函数声明,在类的外部写出函数定义。
*/
class Rect // 定义类
{
private:
int m_iLength;
int m_iWidth;
public:
inline int getLength(); // 方法可以是内联函数
void setLength(int length = 1); // 方法允许有默认值
inline int getWidth();
// 方法允许重载
void setWidth(int width);
void setWidth();
int area();
void show();
};
// 成员函数定义, 返回值 类名::方法名,函数体。
int Rect::getLength()
{
return m_iLength;
}
void Rect::setLength(int length)
{
m_iLength = length;
}
int Rect::getWidth()
{
return m_iWidth;
}
void Rect::setWidth(int width)
{
m_iWidth = width;
}
void Rect::setWidth()
{
m_iWidth = 1;
}
int Rect::area()
{
return m_iLength * m_iWidth;
}
void Rect::show()
{
cout << "m_iLength:" << m_iLength << " &m_iLength: " << &m_iLength;
cout << " m_iWidth:" << m_iWidth << " &m_iWidth: " << &m_iWidth << endl;
}
int main()
{
/*
对象是类的实例,类相当于一种自定义数据类型,对象相当于这种类型的数据。
使用一个类一般首先是创建类的对象,然后操作对象完成计算。
*/
Rect r, r1; //生成Rect类的r1和r2对象。
r.setLength(10);
r.setWidth(2);
cout << r.area() << endl;
r.show();
r1.setLength(5);
r1.setWidth(1);
cout << r1.area() << endl;
r1.show();
Rect r2;
r2.setLength(); // setLength方法的使用默认值。
r2.setWidth(); // setWdith()方法。
cout << r2.area() << endl;
/*
对象可以通过.访问成员
对象指针可以通过->访问成员
*/
Rect* p = &r2;
p->setLength(10);
p->setWidth(100);
cout << p->area() << endl;
return 0;
}
类的特殊成员
构造函数
构造函数的作用
在创建对象时,利用特定的值构造对象,将对象初始化为一个特定的状态。
构造函数的特点
构造函数的函数名与类名相同。
不能定义构造函数的函数类型(即,不能指定构造函数的返回值)。
构造函数应声明为公有函数。
构造函数不能在程序中调用,在对象创建时,构造函数被编译器自动调用。
构造函数可以重载。
如果没有显式定义构造函数,编译器会提供无参默认构造函数。例如: Rect::Rect(){ 函数体为空 }。
//构造函数
class Rect
{
private:
int m_iLength;
int m_iWidth;
public:
Rect();
Rect(int length, int width);
int area();
void show();
};
Rect::Rect()
{
m_iLength = 1;
m_iWidth = 1;
}
Rect::Rect(int length, int width)
{
cout << "Rect::Rect(int length, int width)" << endl;
m_iLength = length;
m_iWidth = width;
}
int Rect::area()
{
return m_iLength * m_iWidth;
}
void Rect::show()
{
cout << "m_iLength:" << m_iLength << " &m_iLength: " << &m_iLength;
cout << " m_iWidth:" << m_iWidth << " &m_iWidth: " << &m_iWidth << endl;
}
int main()
{
Rect r(2, 2);
cout << r.area() << endl;
Rect r1;
cout << r1.area() << endl;
return 0;
}
初始化列表
除了在构造函数中为数据初始化,还可以使用初始化列表对函数成员初始化。
如果类有常量成员数据,则只能使用初始化列表。
一般的数据成员可以使用初始化列表初始化,也可在用构造函数体中赋值。
// 构造列表
class Circle
{
private:
const float m_PI;
int m_radius;
public:
Circle(int r);
int area();
};
/*
常量成员只能在构造列表中被初始化
变量成员即可以在构造函数体,也可以在构造列表中初始化。
*/
Circle::Circle(int r) : m_PI(3.1415926), m_radius(r)
{
// m_radius = r; 变量初始化常用在构造函数体中初始化
}
int Circle::area()
{
return m_PI * m_radius * m_radius;
}
int main()
{
Circle c(2);
cout << c.area() << endl;
return 0;
}
析构函数
由于类中的数据成员可以有指针,对象中可能有动态分配的内存,这就需要在对象消失前将内存释放。C++提供了析构函数来进行内存释放等清理工作。
析构函数的作用
在删除一个对象前被调用,释放该对象成员使用的动态分配内存空间,以及其他一些清理工作。
析构函数的特点
析构函数名称是: ~类名。
析构函数没有类型,也没参数,析构函数不能重载。
一个对象消失时,被编译器自动调用。
如果没有显式提供析构函数,编译器将生成一个默认的函数体为空的析构函数。
// 析构函数
class Circle
{
private:
const float m_PI;
int m_radius;
char* m_name = NULL;
public:
Circle(const char* name, int r);
// 声明析构函数
~Circle();
void show();
};
Circle::Circle(const char* name, int r) : m_PI(3.1415926)
{
cout << name << ", Circle(char* name, int r)" << endl;
m_radius = r;
int len = strlen(name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, name);
}
// 实现析构函数
Circle::~Circle()
{
cout << m_name << ", Circle::~Circle()" << endl;
if (m_name != NULL) delete[]m_name;
}
void Circle::show()
{
cout << m_name << ", " << m_radius << endl;
}
int main()
{
Circle* p = new Circle("p", 1);
char c[10] = "Circle";
Circle circle(c, 2);
circle.show();
c[0] = 'X';
circle.show();
Circle circle1("Cricle1", 3);
delete p;
return 0;
}
拷贝构造函数
拷贝构造函数可以实现用一个已经存在的对象初始化新对象,拷贝构造函数的形参为该类对象的引用。
//拷贝构造
class Circle
{
private:
const float m_PI;
int m_radius;
char* m_name = NULL;
public:
Circle(const char* name, int r);
// 拷贝构造函数的一般格式为 类名(const 类名& 形参名);
Circle(const Circle& circle);
~Circle();
void show();
};
Circle::Circle(const char* name, int r) : m_PI(3.1415926)
{
cout << name << ", Circle(char* name, int r)" << endl;
m_radius = r;
int len = strlen(name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, name);
}
// 拷贝构造函数
Circle::Circle(const Circle& circle) : m_PI(3.1415926)
{
cout << "Circle::Circle(const Circle& circle)" << endl;
m_radius = circle.m_radius;
int len = strlen(circle.m_name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, circle.m_name);
}
Circle::~Circle()
{
cout << m_name << ", Circle::~Circle()" << endl;
if (m_name != NULL) delete[]m_name;
}
void Circle::show()
{
cout << m_name << ", " << m_radius << endl;
}
int main()
{
Circle circle("Circle", 2);
Circle circle1(circle); // 调用拷贝构造函数
Circle circle2 = circle; // 调用拷贝构造函数
return 0;
}
赋值函数
赋值语句把一个对象赋值给另外一个已有的同类对象时,将调用该类的赋值函数。
//赋值函数
class Circle
{
private:
const float m_PI;
int m_radius;
char* m_name = NULL;
public:
Circle(const char* name, int r);
Circle(const Circle& circle);
~Circle();
Circle& operator=(const Circle& circle);
void show();
};
Circle::Circle(const char* name, int r) : m_PI(3.1415926)
{
cout << name << ", Circle(char* name, int r)" << endl;
m_radius = r;
int len = strlen(name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, name);
}
Circle::Circle(const Circle& circle) : m_PI(3.1415926)
{
cout << "Circle::Circle(const Circle& circle)" << endl;
m_radius = circle.m_radius;
int len = strlen(circle.m_name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, circle.m_name);
}
// 赋值函数
Circle& Circle::operator=(const Circle& circle)
{
cout << "Circle::operator=(const Circle& circle)" << endl;
// this是对象的内置变量,其值是对象本身的地址。
if (this != &circle)
{
delete[] m_name;
m_radius = circle.m_radius;
int len = strlen(circle.m_name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, circle.m_name);
}
return *this;
}
Circle::~Circle()
{
cout << m_name << ", Circle::~Circle()" << endl;
if (m_name != NULL) delete[]m_name;
}
void Circle::show()
{
cout << m_name << ", " << m_radius << endl;
}
int main()
{
Circle circle("Circle", 2);
Circle circle1(circle); // 调用拷贝构造函数
Circle circle2 = circle; // 调用拷贝构造函数
Circle circle3("C3", 3);
circle3 = circle; // 调用赋值函数
return 0;
}