C++-构造函数、析构函数、静态成员、常成员

一.构造函数

C++编译器提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。

1.构造函数定义:

C++中的类可以定义为类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
构造函数没有返回类型,在定义时可以有参数;
调用:
自动调用(隐式) 一般情况下C++编译器会自动调用构造函数(无参构造)(默认构造)
手动调用(显示) 在一些情况下则需要手工调用构造函数(有参构造)(重载构造)

2.构造函数的分类:

①合成的默认构造函数(编译器自动生成)

定义一个Human类,内部没有Human()构造函数

#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

// 定义一个“人类”
class Human 
{
public:  //公有的,对外的
	void eat(); //方法, “成员函数”
	void sleep();
	void play();
	void work();

	string getName();
	int getAge();
	int getSalary();

private:
	string name;	//空
	int age = 18;	//类内初始值
	int salary;		//乱值
};

void Human::eat() {
	cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
	cout << "我正在睡觉!" << endl;
}

void Human::play() {
	cout << "我在唱歌! " << endl;
}

void Human::work() {
	cout << "我在工作..." << endl;
}

string Human::getName() {
	return name;
}

int Human::getAge() {
	return age;
}

int Human::getSalary() {
	return salary;
}

int main(void) {
	//绝大多数类都支持无须显式初始化而定义对象,这样的类提供了一个合适的默认值
	Human  h1;  // 使用合成的默认初始化构造函数
	cout << "年龄: " << h1.getAge() << endl;     //使用了类内初始值  result:18
	cout << "薪资:" << h1.getSalary() << endl;  //没有类内初始值,也没有默认初始值  result:乱值
	cout << "名字:" << h1.getName() << endl;    //string没有赋值时,会默认为空 result:空

	system("pause");
	return 0;
}

合成的默认构造函数:没有定义构造函数,编译器会自动生成一个默认构造函数叫合成的默认构造函数,值会使用类内初始值,如果没有那么编译器会给定一个乱值。【C++11】

②自定义的默认构造函数

定义一个Human类,内部含有Human()构造函数

.....
// 定义一个“人类”
class Human {
public:  //公有的,对外的
	Human(); //手动定义的“默认构造函数”
	void eat(); //方法, “成员函数”
	void sleep();
	void play();
	void work();

	string getName();
	int getAge();
	int getSalary();

private:
	string name = "Unknown";//类内初始值
	int age = 28;			//类内初始值
	int salary;				//乱值
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;
}

.....

int main(void) {
	Human  h1;  // 使用自定义的默认构造函数
	cout << "姓名:" << h1.getName() << endl;	//无名氏
	cout << "年龄: " << h1.getAge() << endl;		//18
	cout << "薪资:" << h1.getSalary() << endl;	//30000

	system("pause");
	return 0;
}

手动定义的默认构造函数:如果某数据成员使用类内初始值,同时又在构造函数中进行了初始化,那么以构造函数中的初始化为准。 相当于构造函数中的初始化,会覆盖对应的类内初始值。

③自定义的重载构造函数

定义一个Human类,内部含有带参构造函数,且参数可以只标明类型

.......
// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);	//最好还是加上名字,具有可读性

	string getName();
	int getAge();
	int getSalary();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;
}

Human::Human(int age, int salary) {
	cout << "调用自定义的构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";
}

.......

int main(void) {
	Human  h1(25, 35000);  // 使用自定义的重载构造函数,显式调用

	cout << "姓名:" << h1.getName() << endl;
	cout << "年龄: " << h1.getAge() << endl;
	cout << "薪资:" << h1.getSalary() << endl;

	system("pause");
	return 0;
}

#endif

④合成的拷贝构造函数(编译器自动生成)

定义一个Human类,内部不含Human(const Human&);拷贝构造函数,这样编译器生成的拷贝构造函数为浅拷贝

.....

// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);		//像这种声明,一般还是加上名字,有可读性
	//不写名字最好
	//Human(const Human&);  //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”,合成的通常会有问题,因为是(位)浅拷贝

	string getName();
	int getAge();
	int getSalary();
	
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	
	char* addr;
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;
}

Human::Human(int age, int salary) {
	cout << "调用自定义的构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";

	addr = new char[64];
	strcpy_s(addr, 64, "China");
}

.....

void Human::setAddr(const char* newAddr) {
	if (!newAddr) {
		return;
	}

	strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
	return addr;
}


int main(void) {
	Human  h1(25, 35000);  	// 使用自定义的重载构造函数
	Human  h2(h1);			// 使用合成的拷贝构造函数,利用h1的值构建新的h2
	Human  h3 = h1;			// 使用合成的拷贝构造函数,利用h1的值构建新的h2

	cout << "h1 addr:" << h1.getAddr() << endl;	//China
	cout << "h2 addr:" << h2.getAddr() << endl;	//China
	cout << "h3 addr:" << h3.getAddr() << endl;	//China

	h1.setAddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;	//长沙
	cout << "h2 addr:" << h2.getAddr() << endl;	//长沙
	cout << "h3 addr:" << h3.getAddr() << endl;	//长沙

	system("pause");
	return 0;
}

结果:合成的拷贝构造函数的缺点: 使用“浅拷贝”,在对不是指针的变量时,没什么影响,但是一旦涉及到指针,那么就会出现问题。h2、h3会跟着h1变化,因为h2、h3指针指向h1。

⑤自定义的拷贝构造函数

定义一个Human类,内部含Human(const Human&);拷贝构造函数,当出现指针变量时有机会进行深拷贝

.......
// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);

	Human(const Human&);	//定义拷贝构造函数

	void setaddr(const char* addr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	char* addr;
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;
}

Human::Human(int age, int salary) {
	cout << "调用自定义的构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name= "无名";

	addr = new char[64];
	strcpy_s(addr, 64, "china");
}

//一个类只能出现一个拷贝构造函数
Human::Human(const Human& man) {				//man相当于h1的引用
	cout << "调用自定义的拷贝构造函数" << endl;
	name = man.name;
	age = man.age;
	salary = man.salary;
	//深度拷贝
	addr = new char[64];
	strcpy_s(addr, 64, man.addr);
}

....

void Human::setaddr(const char* newaddr)
{
	if (!newaddr)
	{
		return;
	}
	strcpy_s(addr, 64, newaddr);
}

const char* Human::getAddr()
{
	return addr;
}

int main(void) {
	Human  h1(25, 35000);  // 使用自定义的重载构造函数
	Human  h2(h1);  	// 使用自定义的拷贝构造函数
	Human  h3 = h1;  	// 使用自定义的拷贝构造函数

	cout << "h1 addr:" << h1.getAddr() << endl;	//china
	cout << "h2 addr:" << h2.getAddr() << endl;	//china

	h1.setaddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;	//长沙
	cout << "h2 addr:" << h2.getAddr() << endl;	//china

	system("pause");
	return 0;
}

结果:这一次h2没有跟着h1变化,因为h2拷贝构造时申请了一片空间,这片空间拷贝了h1的addr值,所以h1变化不会导致h2变化

关于何时调用拷贝构造函数

  1. 返回类型不是类,且实参是对象,形参也是对象
    如果函数的形参是引用类型,就不会调用拷贝构造函数
//此时man就是一个别名,因此不会调用拷贝构造,如果去掉引用则会调用拷贝构造
void test2(const Human& man) { 
	cout << __FUNCTION__ << endl;
	//cout << man.getSalary() << endl;
}

main:
test2(h1);
  1. 返回类型是类,而且不是引用类型
    如果函数的返回类型是引用类型的类,就不会调用拷贝构造函数
    注意:和上一个对应,如果返回类型是类,且实参是对象,形参是引用类型,也会调用拷贝构造函数,所以对于返回类型是类的,怎么样都可以,对于返回类型不是类的,那么就要看实参和形参了
//返回类型是类,调用拷贝构造函数
Human test3(Human& man) {
	cout << __FUNCTION__ << endl;
	return man;
}
//返回类型是类的引用,加const更好,防止被改,返回类的引用,不调用拷贝构造
Human& test4(Human& man) {
	cout << __FUNCTION__ << endl;
	return man;
}

main:
test3(h1);
test4(h1);
  1. 对象数组的初始化列表中,使用对象。
	Human men[] = { h1, h2, h3 }; //调用3次拷贝构造函数

完整代码:

.......

// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);
	Human(const Human&);  //定义自定义拷贝构造函数

	string getName();
	int getAge();
	int getSalary() const;
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	char* addr;
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;
}

Human::Human(int age, int salary) {
	cout << "调用自定义的构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";

	addr = new char[64];
	strcpy_s(addr, 64, "China");
}

Human::Human(const Human& man) {
	cout << "调用自定义的拷贝构造函数" << "参数:" << &man
		<< " 本对象:" << this << endl;

	age = man.age;      //this是一个特殊的指针,指向这个对象本身
	salary = man.salary;
	name = man.name;
	// 深度拷贝
	addr = new char[64];
	strcpy_s(addr, 64, man.addr);
}

string Human::getName() {
	return name;
}

int Human::getAge() {
	return age;
}

int Human::getSalary() const {
	return salary;
}

void Human::setAddr(const char* newAddr) {
	if (!newAddr) {
		return;
	}

	strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
	return addr;
}

//在进行参数传递时,自动执行:Human man = h1;
void test(Human man) {
	cout << __FUNCTION__ << endl;
	//cout << man.getSalary() << endl;
}

//形参不是引用类型,因为就是一个别名,不会调用拷贝构造函数,此时没有没有构造新的对象
//加一个const最好,这样能保证不需要改变实参时,调用这个引用是不会去改变实参
void test2(const Human& man) { 
	cout << __FUNCTION__ << endl;
	//cout << man.getSalary() << endl;
}
//返回类型是类,调用拷贝构造函数
Human test3(Human& man) {
	cout << __FUNCTION__ << endl;
	return man;
}
//返回类型是类的引用,加const更好,防止被改,返回类的引用,不调用拷贝构造
Human& test4(Human& man) {
	cout << __FUNCTION__ << endl;
	return man;
}

//调用拷贝构造
Human test5(const Human& man, const Human& man2)
{
	cout << __FUNCTION__ << endl;
	if (man.getSalary() > man2.getSalary())
	{
		return man;
	}
	else
	{
		return man2;
	}
}

//返回类型是类的引用,防止被改(因为参数是const)
const Human& test6(const Human& man, const Human& man2)
{
	cout << __FUNCTION__ << endl;
	if (man.getSalary() > man2.getSalary())
	{
		return man;
	}
	else
	{
		return man2;
	}
}
int main(void) {
	Human h1(25, 35000);  // 调用自定义构造函数
	Human h2(h1);         // 调用拷贝构造函数,参数h1:00DBFCA0 本对象:00DBFC70
	Human h3 = h1;		  // 调用拷贝构造函数,参数h1:00DBFCA0 本对象:00DBFC40

	test(h1);		      // 为形参对象准备--调用拷贝构造函数,参数h1:00DBFCA0 本对象:00DBF8E0
	test2(h1);			  // 不会调用拷贝构造函数,形参是引用

	test3(h1);            // 创建一个临时对象,接收test3函数的返回值,调用1次拷贝构造函数,函数参数h1:00DBFCA0 本对象:00DBF9A4
	test33(h1);			  // 调用两次拷贝构造,第一次是为形参调用,参数:00DBFCA0 本对象:00DBF8E0;第二次是为返回的对象调用,参数:00DBF8E0 本对象:00DBF968
	Human h4 = test3(h1); // 为返回值调用1次拷贝构造函数,参数h1:00DBFCA0 本对象:00DBFC10,返回的对象直接作为h4的拷贝构造函数的参数即&h4:00DBFC10;如果test3形参不是引用类型,那么调用两次拷贝构造,h4对象将会是形参对象

	test4(h1);            // 因为返回的是引用类型,所以不会创建临时对象,不会调用拷贝构造函数
	test44(h1);			  // 为形参调用1次拷贝构造,参数:00DBFCA0 本对象:00DBF8E0

	Human h5(26, 40000);  //00DBFBE0
	test5(h1, h5);		  //调用1次拷贝构造函数(这里会为返回值拷贝h5),参数:00DBFBE0 本对象:00DBF92C

	test6(h1, h5);		  // 因为返回的是引用类型,所以不会创建临时对象,不会调用拷贝构造函数

	Human men[] = { h1, h2, h3 }; //调用3次拷贝构造函数,参数h1:00DBFCA0 本对象:00DBFB60;参数h2:00DBFC70 本对象:00DBFB88;参数h3:00DBFC40 本对象:00DBFBB0

	system("pause");
	return 0;
}

⑥赋值构造函数(内含this指针用法)

赋值拷贝本质是运算符重载,返回值一定是对象的引用,参数一定是常量引用:Human& operator=(const Human&);
注意和拷贝构造函数的区分:
创建对象并进行初始化是拷贝构造Human h2 = h1;,已经创建好进行初始化是赋值构造h2 = h1;

.......

#define ADDR_LEN 64

// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);
	Human(const Human&);  //拷贝构造函数

	//赋值拷贝,运算符重载,返回值一定是对象的引用,参数一定是常量引用
	Human& operator=(const Human&);

	//析构函数
	~Human();

	string getName();
	int getAge();
	int getSalary();
	void setAddr(const char* newAddr);
	const char* getAddr();

	//this指针的案例
	Human& compare(Human&);
	Human* compare2(Human*);

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	char* addr;
};

Human::Human() {
	cout << "调用自定义的无参构造函数" << endl;
	name = "无名氏";
	age = 18;
	salary = 30000;
	addr = new char[64];
	strcpy_s(addr, 64, "China");
}

Human::Human(int age, int salary) {
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";

	addr = new char[64];
	strcpy_s(addr, 64, "China");
}

Human::Human(const Human& man) {
	cout << "调用自定义的拷贝构造函数" << "参数:" << &man
		<< " 本对象:" << this << endl;

	age = man.age;      //this是一个特殊的指针,指向这个对象本身
	salary = man.salary;
	name = man.name;
	// 深度拷贝
	addr = new char[64];
	strcpy_s(addr, 64, man.addr);
}

//运算符重载,返回值一定是对象的引用,参数一定是常量引用
Human& Human::operator=(const Human& man) {
	cout << "调用" << __FUNCTION__ << endl;
	//检测是不是对自己赋值:比如 h1 = h1;
	if (this == &man) {
		return *this;
	}

	// 如果有必要,需要先释放自己的资源(动态内存)
	//delete addr;
	//addr = new char[ADDR_LEN];

	// 深拷贝,不是拷贝的指针本身,而是拷贝的指向内存
	strcpy_s(addr, ADDR_LEN, man.addr);

	// 处理其他数据成员
	this->name = man.name;
	age = man.age;
	salary = man.salary;

	// 返回该对象本身的引用, 以便做链式连续处理,比如 a = b = c;
	return *this;//this是指针指向这个对象,*this是对象本身
}
Human::~Human()
{
	cout << "调用析构函数" <<this<< endl;
	delete addr;
}

.........

void test(Human man) {
	cout << man.getSalary() << endl;
}

void test2(Human& man) { //不会调用拷贝构造函数,此时没有没有构造新的对象
	cout << man.getSalary() << endl;
}

Human test3(Human& man) {
	return man;
}

Human& test4(Human& man) {
	return man;
}

void test5()
{
	Human h1;
	{
		Human h2;
	}
	cout << "test().end" << endl;
}
//this是地址,*this是取到地址的值
//this指针的案例:返回值为对象的引用
Human& Human::compare(Human& other)
{
	if (salary > other.salary)
	{
		return *this;
	}
	else
	{
		return other;
	}
}
//this指针的案例,返回值为对象的指针
Human* Human::compare2(Human* other)
{
	if (salary > other->salary)
	{
		return this;
	}
	else
	{
		return other;
	}
}


int main(void) {
	Human h1(25, 35000);  

	// 特别注意,此时是创建对象h2并进行初始化,调用的是拷贝构造函数,
	// 不会调用赋值构造函数
	Human h2 = h1;

	h2 = h1;		//调用赋值构造函数,此时里面的形参&man=&h1,this=&h2也就是说调用者是h2,h1作为参数传进去
	h2 = test3(h1); //为返回值调用1次拷贝构造函数

	Human h3 = test3(h1); //调用拷贝构造函数

	test5();

	Human h4(26, 40000);
	cout <<&(h1.compare(h2)) << endl;	//形参为对象的引用
	cout <<&(h1) << endl;
	cout <<&(h4) << endl;

	Human* p = &h1;
	cout << (p->compare2(&h2)) << endl;	//形参为对象的指针

	system("pause");
	return 0;
}

二.析构函数

定义:
C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数

  • 析构函数没有参数也没有任何返回类型的声明
  • 析构函数在对象销毁时自动被调用

具体的清理工作,一般和构造函数对应,比如:如果在构造函数中,使用new分配了内存,就需在析构函数中用delete释放。

如果构造函数中没有申请资源(主要是内存资源),
那么很少使用析构函数。

函数名:
~类型
没有返回值,没有参数,最多只能有一个析构函数

访问权限:
一般都使用public

使用方法:
不能主动调用。
对象销毁时,自动调用。
如果不定义,编译器会自动生成一个析构函数(什么也不做)

#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个“人类”
class Human {
public:
	Human();
	Human(int age, int salary);
	Human(const Human&);  //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”
	Human& operator=(const Human &);
	~Human(); //析构函数
     ......

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	char *addr;
};

Human::Human() {
	name = "无名氏";
	age = 18;
	salary = 30000;

	addr = new char[64];
	strcpy_s(addr, 64, "China");
	cout << "调用默认构造函数-" << this << endl;
}

......

Human::~Human() {
	cout << "调用析构函数-" << this  << endl;  //用于打印测试信息
	delete addr;
}

void test() {
	Human h1;

	{
		Human h2;
	}
	cout << "test()结束" << endl;
}

int main(void) {
	test();

	system("pause");
	return 0;
}

三.静态成员

1.静态数据成员

需要获取总的人数,如何实现?
只能使用一个全局变量,然后在构造函数中对这个全局变量进行修改(加1)
缺点:使用全局变量不方便,破坏程序的封装性。

解决方案:
使用类的静态成员。

Human.h

class Human {
public:
	......
int getCount();
private:
	string name = "Unknown";
	int age = 28;
	......

	// 类的静态成员
	static int count;
};

Human.cpp

#include "Human.h"	

// 初始化类的静态成员
int Human::count = 0;
......

Human::Human() {
	cout << "调用构造函数:" << this << endl;
	name = "无名氏";
	age = 18;
	salary = 30000;

	addr = new char[ADDR_LEN];
	strcpy_s(addr, ADDR_LEN, "China");

	count++;
}

// 类的普通成员函数,可以直接访问静态成员(可读可写)
int Human::getCount() {
	return count;
}

main.cpp

#include "Human.h"

int main(void) {
	Human h1;
	cout << h1.getCount() << endl;

	Human h2;
	cout << h1.getCount() << endl;

	system("pause");
	return 0;
}

注意:对于非const的类静态成员,只能在类的cpp实现文件中初始化。 或者单文件中则在类的外面进行初始化。
const类静态成员,可以在类内设置初始值,也可以在类的实现文件中设置初始值。(但是不要同时在这两个地方初始化,只能初始化1次)

2.静态成员函数

上一节getCount的讨论:
当需要获取总的人数时,还必须通过一个对象来访问,比如h1.getCount().
如果当前没有可用的对象时,就非常尴尬,不能访问getCount()!
如果为了访问总的人数,而特意去创建一个对象,就很不方便,
而且得到的总人数还不真实(包含了一个没有实际用处的人)

解决方案:
把getCount()方法定义为类的静态方法!

类的静态方法:

  1. 可以直接通过类来访问【更常用】,也可以通过对象(实例)来访问。
  2. 类的静态方法中,可以访问静态数据成员和方法,但是不能访问普通数据成员和普通成员函数(对象的数据成员和成员函数)

Human.h

#pragma once
......
class Human {
public:
    ......
	static int getCount();
    ......
};

Human.cpp

......

//静态方法的实现,不能加static
int Human::getCount() { 
	//  静态方法中,不能访问实例成员(普通的数据成员)
	// cout << age;

	// 静态方法中,不能访问this指针
	// 因为this指针是属于实例对象的
	// cout << this;

	//静态方法中,只能访问静态数据成员
	return count;
}
......

main.cpp

void test() {
	cout << "总人数: ";
	// ??? 没有可用的对象来访问getCount()

	// 直接通过类名来访问静态方法!
	// 用法:类名::静态方法
	cout << Human::getCount(); 
}

int main(void) {
	Human h1, h2;

	test();

	system("pause");
	return 0;
}

总结

1)静态数据成员
对象的成员函数(没有static的成员函数)内部,可以直接访问“静态数据成员”
类的静态成员函数(有static的成员函数)内部,可以直接访问“静态数据成员”
即:所有的成员函数,都可以访问静态数据成员。

类不能直接访问普通的静态数据成员(Human::humanCount 非法)

2)静态成员函数
对象可以直接访问静态成员函数
类可以直接访问静态成员函数(Human::getHumanCount())
在类的静态成员函数(类的静态方法)内部,不能直接访问this指针和对象的数据成员!
在类的静态成员函数(类的静态方法)内部,只能访问类的静态数据成员

四.常成员

1.常数据成员

怎样表示人的“血型”?
血型可以修改吗?

解决方案:
把血型定义为const数据类型(常量数据成员)

const数据成员的初始化方式:

  1. 使用类内值(C++11支持)
  2. 使用构造函数的初始化列表
    (如果同时使用这两种方式,以初始化列表中的值为最终初始化结果)
    注意: 不能在构造函数或其他成员函数内,对const成员赋值!

Human.h

#pragma once
......
class Human {
public:
    ......
private:
    ......
	const string bloodType;
};

Human.cpp

// 使用初始化列表,对const数据成员初始化
Human::Human():bloodType("未知") {
     ......

	//在成员函数内,不能对const数据成员赋值
	//bloodType = "未知血型";
	count++;
}

void Human::description() const {
	cout << "age:" << age
		<< " name:" << name
		<< " salary:" << salary
		<< " addr:" << addr 
		<< " bloodType:" << bloodType << endl; //其他成员函数可以“读”const变量
}

Main.cpp

int main(void) {
	Human h1;

	h1.description();

	system("pause");
	return 0;
}

2.常成员函数

const的Human对象,不能调用普通的成员函数。

分析:
C++认为,const(常量)对象,如果允许去调用普通的成员函数,而这个成员函数内部可能会修改这个对象的数据成员!而这将导致const对象不再是const对象!

解决方案:
如果一个成员函数内部,不会修改任何数据成员,就把它定义为const成员函数。

Human的description方法

//Human.h
class Human {
public:
    ......
	void description() const;  //注意,const的位置
    ......
};

//Human.cpp
void Human::description ()const {
	cout << "age:" << age
		<< " name:" << name
		<< " salary:" << salary
		<< " addr:" << addr 
		<< " bloodType:" << bloodType << endl;
}

//main.cpp
int main(void) {
	const Human h1;	//常对象访问常成员函数
	h1.description();
	
	system("pause");
	return 0;
}

const成员函数内,不能修改任何数据成员!

C++的成员函数设置建议:
如果一个对象的成员函数,不会修改任何数据成员,那么就强烈:
把这个成员函数,定义为const成员函数!

总结:

函数传参:非const引用(自由引用), 不能对const变量进行引用
const对象只能调用const成员方法
const引用, 可以对非const变量进行引用

#include <iostream>
#include <windows.h>

using namespace std;

class Man{
public:
	Man(){}
	void play() const {
		cout << "I am playing ...." << std::endl;
	}
};

void test(const Man &man) {
	man.play();
}
void test2(Man& man)
{
	man.play();
}
int main(void) {
	const Man man;
	test(man);	//const引用, 可以对const变量进行引用
	test2(man);	//报错,非const引用(自由引用),  不能对const变量进行引用
	
    Man man2;
    test(man2);	//const引用, 可以对非const变量进行引用
    test2(man2);//非const引用(自由引用),  能对非const变量进行引用
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值