C++(2)类与对象简介(A)

目录

一、面向对象程序设计的基本特点

二、类和对象

三、构造函数

四、析构函数


一、面向对象程序设计的基本特点

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();
}

猜你喜欢

转载自blog.csdn.net/zjuwxx/article/details/81392463