目录
一、面向对象程序设计的基本特点
1)抽象
对同一类对象的共同属性和行为进行概括,形成类
·首先注意问题的本质及描述,其次是实现过程或细节
·属性抽象:描述某类对象的属性或状态(对象之间相互区别的物理量)
·行为抽象:描述某类对象的共有的行为特征或具有的功能
·抽象的实现:类
2)封装
将抽象出的数据、代码封装在一起,形成类
·目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员
·实现封装:类声明中的 { }
3)继承
在已有类的基础上,进行扩展形成新的类
4)多态
同一名称,不同的功能实现方式
·目的:达到行为标识统一,减少程序中标识符的个数
·实现:重载函数和虚函数
二、类和对象
对象:现实中对象的模拟,具有属性和行为
类:同一类对象的共同属性和行为
对象是类的实例
·对类名或类对象应用sizeof运算符时只会返回类的数据成员的规模
·编译器只创建一份独立于该类的所有对象的成员函数副本。类的所有对象共享这一副本
举例:
#include<iostream>
using namespace std;
class Clock {
public:
void setTime(int newH = 0, int newM = 0, int newS = 0);
void showTime();
private:
int hour, minute, second;
};
void Clock::setTime(int newH, int newM, int newS) {
hour = newH, minute = newM, second = newS;
}
void Clock::showTime() {
cout << hour << ":" << minute << ":" << second << endl;
}
int main() {
Clock myClock; //myClock是类Clock的对象
myClock.setTime(8, 30, 30);
myClock.showTime();
return 0;
}
结果:
8:30:30
三、构造函数
1)作用
·在对象被创建时使用特定的值构造对象,如,在构造一个 Clock 类对象时,将初始时间设为0:0:0
2)形式
·函数名与类名相同
·不能定义返回值类型
·形式参数可有可无
·可以是内联函数
·可以重载
·可以带默认参数值
注:构造函数在对象创建时被自动调用
3)默认构造函数
1.调用时可以不需要实参的构造函数
·参数表为空的构造函数
·全部参数都有默认值的构造函数
2.下面两个都是默认构造函数,如在类中同时出现,将产生编译错误
Clock();
Clock(int newH = 0, int newM = 0, int newS = 0);
3.如果程序中未定义构造函数,编译器将在需要时自动生成
·参数列表为空,不为数据成员设置初始值
·如果类内定义了成员的初始值,则使用类内定义的初始值;如果没有定义类内的初始值,则以默认方式初始化
·基本类型的数据默认初始化的值是不确定的
4."=default"
·如果程序中已定义构造函数,默认情况下编译器就不再隐含生成默认构造函数。如果此时依然希望编译器隐含生成默认构造函数,可以使用 "=default"
class Clock {
public:
Clock() = default; //指示编译器提供默认构造函数
Clock(int newH, int newM, int newS); //构造函数
private:
int hour, minute, second;
};
4)委托构造函数
·类中往往有多个构造函数,只是参数列表和初始化列表不同,其初始化算法相同,为了避免代码重复,可以使用委托构造函数
·委托构造函数使用类的其他构造函数执行初始化过程
Clock(int newH, int newM, int newS): hour(newH),minute(newM),second(newS) { }
Clock(): Clock(0, 0, 0) { }
5)复制构造函数
1.说明
·形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象
·复制构造函数不一定要定义成一个完成复制功能的函数,完全由自己决定
class 类名 {
public:
类名(形参); //构造函数
类名(const 类名 &对象名); //复制构造函数
...
};
类名::类(const 类名 &对象名) { 函数体 } //复制构造函数的实现
2.隐含的复制构造函数
·如果类中没有显式的声明复制构造函数,则编译器自动生成一个隐含的复制构造函数
·功能:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员
3."=delete"
如果不希望对象被复制构造,用 "=delete" 指示编译器不生成默认复制构造函数
class Point {
public:
Point(int xx = 0, int yy = 0) { x = xx; y = yy; } //构造函数,内联
Point(const Point& p) = delete; //指示编译器不生成默认复制构造函数
private:
int x, y;
};
4.被调用的三种情况
·用类的一个对象去初始化另一个对象时
·当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
·当函数的返回值是类的对象或引用时
举例:
#include<iostream>
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); //构造函数
Clock(); //默认构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(int newH, int newM = 0, int newS = 0) : hour(newH), minute(newM), second(newS) { }
Clock::Clock() : hour(0), minute(0), second(0) { }
void Clock::setTime(int newH, int newM, int newS) {
hour = newH, minute = newM, second = newS; //注意这种赋值方式,只有一个分号!
}
void Clock::showTime() {
cout << hour << ":" << minute << ":" << second << endl;
}
int main() {
Clock c1(6, 7); //调用有参数的构造函数
//非默认构造函数与默认构造函数同时调用时,前者的参数列表不能全部事先设置默认值,否则编译器会显示ambiguous而报错
c1.showTime();
Clock c2; //调用无参数的构造函数
c2.showTime();
c1.setTime(3, 5, 18);
c1.showTime();
return 0;
}
结果:
6:7:0
0:0:0
3:5:18
四、析构函数
说明
·一个类只能有一个析构函数,不允许重载析构函数。析构函数必须是 public 类型
·在删除对象时被隐式调用,完成一些清理工作,然后释放此对象所属的空间
·如果程序中未声明析构函数,编译器将自动产生一个默认析构函数
构造函数与析构函数的调用顺序
通常,析构函数的调用顺序与构造函数相反,但对象的存储类别可以改变析构函数的调用顺序
·在全局作用域内定义的对象的构造函数在该文件中的任何其他函数(包括 main 函数)开始执行之前执行。在 main 函数终止时调用相应的析构函数
·自动局部变量的构造函数在执行到达定义该对象的程序点时调用,对应的析构函数是在对象离开所在作用域时(即定义该对象的块执行结束时)调用
·static 局部对象的构造函数只在执行第一次到达定义对象的程序点时调用一次,对应的析构函数在 main 函数终止或调用 exit 函数时调用,若通过调用 abort 函数终止程序,则不调用 static 对象的析构函数
举例:
#include<iostream>
#include<string>
using namespace std;
class A {
public:
A(int, string);
~A();
private:
int number;
string message;
};
A::A(int num, string mes) {
number = num;
message = mes;
cout << "number " << number << " constructor runs\t" << message << endl;
}
A::~A() {
cout << "number " << number << " destructor runs\t" << message << endl;
}
void create();
A first(1, "(global before main)");
int main() {
cout << "\nMAIN function begins!!!\n";
A second(2, "(local automatic in main)");
static A third(3, "(local static in main)");
create();
cout << "Main function resumes!!\n";
A forth(4, "(local automatic in main)");
cout << "MAIN function ends!!!\n\n";
return 0;
}
void create() {
cout << "\nLocal function begins!\n";
A fifth(5, "(local automatic in create)");
static A sixth(6, "(local static in create)");
A seventh(7, "(local automatic in create)");
cout << "Local function ends!\n\n";
}
结果:
number 1 constructor runs (global before main)
MAIN function begins!!!
number 2 constructor runs (local automatic in main)
number 3 constructor runs (local static in main)
Local function begins!
number 5 constructor runs (local automatic in create)
number 6 constructor runs (local static in create)
number 7 constructor runs (local automatic in create)
Local function ends!
number 7 destructor runs (local automatic in create)
number 5 destructor runs (local automatic in create)
Main function resumes!!
number 4 constructor runs (local automatic in main)
MAIN function ends!!!
number 4 destructor runs (local automatic in main)
number 2 destructor runs (local automatic in main)
number 6 destructor runs (local static in create)
number 3 destructor runs (local static in main)
number 1 destructor runs (global before main)