目录
一、面向对象程序设计的基本特点
1)抽象
对同一类对象的共同属性和行为进行概括,形成类
·首先注意问题的本质及描述,其次是实现过程或细节
·数据抽象:描述某类对象的属性或状态(对象之间相互区别的物理量)
·代码抽象:描述某类对象的共有的行为特征或具有的功能
·抽象的实现:类
//抽象实例——钟表
//数据抽象:
int hour, int minute, int second
//代码抽象:
setTime(), showTime()
//代码
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
2)封装
将抽象出的数据、代码封装在一起,形成类
·目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员
·实现封装:类声明中的 { }
class Clock {
public: //特定的访问权限
void setTime(int newH, int newM, int newS);
void showTime(); //setTime,showTime作为外部接口
private: //特定的访问权限
int hour, minute, second;
};
3)继承(待补充)
在已有类的基础上,进行扩展形成新的类
4)多态(待补充)
同一名称,不同的功能实现方式
·目的:达到行为标识统一,减少程序中标识符的个数
·实现:重载函数和虚函数
二、类和对象
对象:现实中对象的模拟,具有属性和行为
类:同一类对象的共同属性和行为
对象是类的实例
举例:
#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;
}
三、构造函数
1)作用
·在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态。如,希望在构造一个Clock类对象时,将初试时间设为0:0:0,就可以通过构造函数来设置
2)形式
·函数名与类名相同
·不能定义返回值类型,也不能有return语句
·可以有形式参数,也可以没有形式参数
·可以是内联函数
·可以重载
·可以带默认参数值
注:构造函数在对象创建时被自动调用
Clock myClock(0,0,0);
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;
};
举例:
#include<iostream>
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); //构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(int newH = 0, int newM = 0, int newS = 0) : hour(newH), minute(newM), second(newS) { }
void Clock::showTime() {
cout << hour << ":" << minute << ":" << second << endl;
}
void Clock::setTime(int newH, int newM, int newS) {
hour = newH, minute = newM, second = newS; //注意这种赋值方式,只有一个分号!
}
int main() {
Clock c(8); //调用构造函数
// Clock c(); 调用错误,成员函数showTime也不需要参数,编译器不知道应该调用哪一个
// Clock c(3,2,35); 对象初始化只能进行一次,超出则报错
c.showTime();
c.setTime(3, 5, 18);
c.showTime();
return 0;
}
//修改版
#include<iostream>
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); //构造函数
Clock(); //默认构造函数
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::showTime() {
cout << hour << ":" << minute << ":" << second << endl;
}
int main() {
Clock c1(6, 7, 32); //调用有参数的构造函数
//非默认构造函数与默认构造函数同时调用时,前者的参数列表不能全部事先设置默认值,否则编译器会显示ambiguous而报错
c1.showTime();
Clock c2; //调用无参数的构造函数
c2.showTime();
return 0;
}
4)委托构造函数
·类中往往有多个构造函数,只是参数列表和初始化列表不同,其初始化算法都是相同的,为了避免代码重复,可以使用委托构造函数
//Clock类的两个构造函数:
Clock(int newH, int newM, int newS) : hour(newH),minute(newM),second(newS) { }
Clock::Clock(): hour(0),minute(0),second(0) { }//默认构造函数
·委托构造函数使用类的其他构造函数执行初始化过程
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"
如果不希望对象被复制构造
·C++98做法:将复制构造函数声明为private,并且不提供函数的实现
·C++11做法:用“=delete”指示编译器不生成默认复制构造函数
class Point { //Point 类的定义
public:
Point(int xx = 0, int yy = 0) { x = xx; y = yy; } //构造函数,内联
Point(const Point& p) = delete; //指示编译器不生成默认复制构造函数
private:
int x, y; //私有数据
};
4.复制构造函数被调用的三种情况
·定义一个对象时,以本类另一个对象作为初始值,发生复制构造
·如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
·如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造(这种情况也可以通过移动构造避免不必要的复制)
举例:
#include <iostream>
using namespace std;
//复制构造函数只有一个参数,即对同类对象的引用
//形如 X::X(X & ..)或X::X(const X &..),二者选一
//如果没有定义复制构造函数,编译器会生成默认复制构造函数,默认是完成复制功能
//复制构造函数不一定要定义成一个完成复制功能的函数,完全由自己决定
//第一种情况:
class Complex {
public:
double real, imag;
Complex() {} //自定义无参构造函数
Complex(const Complex & c) {
real = c.real;
imag = c.imag;
cout << "Copy Constructor called\n";
}
};
//第二种情况:
class A {
public:
double value;
A() {}; //定义构造函数
A(A & a) {
value = a.value + 1; //也可以什么都不写
cout << "Copay constructor A called\n";
}
};
void Func(A a1) { //定义一个函数,他的形参是类A的对象
cout << a1.value << endl; //调用该函数时,类A的复制构造函数将被调用
}
//第三种情况:
class B {
public:
int v;
B(int n) { v = n; }; //定义构造函数
B(const B & a) { //定义复制构造函数
v = a.v;
cout << "Copy constructor B called\n";
}
};
B Funcb() {
B b(4);
return b; //如果函数的返回值是类B的对象时,在函数返回时,B的复制构造函数将被调用
}
int main()
{
cout << "第一种情况:";
//复制构造函数起作用的三种情况
Complex c1;
c1.imag = 3;
c1.real = 4;
Complex c2(c1);
//Complex c2=c1; //初始化语句,不是赋值语句
cout << c2.imag << "\n" << c2.real << endl;
cout << "第二种情况:";
A a2; //a2是类A的一个对象
a2.value = 3;
Func(a2); //Func函数,他的形参是类A的对象
cout << a2.value << endl; //则调用该函数时,类A的复制构造函数将被调用
//此处,复制构造函数将a2.value+1复制给a1,Func函数完成输出功能
cout << "第三种情况:";
cout << "\n" << Funcb().v << endl; //函数返回值是类的对象时,在函数返回时,该类的复制构造函数将被调用
return 0;
}
四、析构函数
说明
·完成对象被删除前的一些清理工作
·在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间
·如果程序中未声明析构函数,编译器将自动产生一个默认析构函数,其函数体为空
class Point {
public:
Point(int xx,int yy);
~Point();
//其他函数原型
private:
int x, y;
};
Point::Point(int xx, int yy) {
x = xx, y = yy;
}
Point::~Point() { }
//其他函数
举例:
#include<iostream>
#include<string>
using namespace std;
class Critter {
public:
Critter(const string& name = "", int age = 0);
~Critter(); //destructor prototype
Critter(const Critter& c); //copy constructor prototype
Critter& operator = (const Critter& c); //overloaded assignment op
void Greet() const;
/* data */
private:
string * m_pName;
int m_Age;
};
Critter::Critter(const string& name, int age) {
cout << "constructor called." << endl;
m_pName = new string(name);
m_Age = age;
}
Critter::~Critter() {
cout << "Destructor called." << endl;
delete m_pName;
}
Critter::Critter(const Critter& c) {
cout << "Copy constructor called." << endl;
m_pName = new string(*(c.m_pName));
m_Age = c.m_Age;
}
Critter& Critter::operator = (const Critter& c) {
cout << "Overloaded assignment operator called." << endl;
if (this != &c) {
delete m_pName;
m_pName = new string(*(c.m_pName));
m_Age = c.m_Age;
}
return *this;
}
void Critter::Greet() const {
cout << "I'm " << *m_pName << " and I'm " << m_Age << " years old." << endl;
cout << "&m_pName: " << &m_pName << endl;
}
void testDestructor();
void testCopyConstructor(Critter aCopy);
void testAssignmentOp();
//主函数
int main(int argc, char const *argv[]) {
testDestructor();
cout << endl;
Critter crit("Poochie", 5);
crit.Greet();
testCopyConstructor(crit);
crit.Greet();
cout << endl;
testAssignmentOp();
return 0;
}
void testDestructor() {
Critter toDestroy("Rover", 3); //创建对象在栈中分配内存
toDestroy.Greet();
}
void testCopyConstructor(Critter aCopy) {
aCopy.Greet();
}
void testAssignmentOp() {
Critter crit1("crit1", 7);
Critter crit2("crit2", 9);
crit1 = crit2;
crit1.Greet();
crit2.Greet();
cout << endl;
Critter crit3("crit3", 11);
crit3 = crit3;
crit3.Greet();
}