二代征信 function soap css获取最后一个元素 linux源码在线阅读 matlab中log函数 配置tomcat环境变量 matlab输入参数太多 python3教程 python环境变量配置 python处理json文件 java接口的使用 java获取ip地址 linux中sudo 网页游戏代码 磁盘分区软件 coreldraw11 骁龙660和625 bat脚本 丁丁下载 stretchcolumns dll下载 VSPD 工信部手机入网查询 ansys安装教程 medcalc js代码混淆工具 处理器虚拟化技术 cad自动保存位置 js字符串比较 五笔字型86版 alert换行 微信预约系统 dns劫持怎么解决 微信群群发软件 召唤加点90刷图加点 抖音道具 深度学习pdf dh浩劫天赋 苹果手机怎么添加邮箱
当前位置: 首页 > 学习教程  > 编程语言

C++学习笔记11,C++核心编程

2021/2/13 20:04:51 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

C学习笔记11,C核心编程 目录C学习笔记11,C核心编程对象的初始化和清理1、构造函数和析构函数2、构造函数的分类及调用3、拷贝构造函数调用时机4、构造函数调用规则5、深拷贝与浅拷贝6、初始化列表7、类对象作为类成员8、静态成员对象的初始化和清理 对象…

C++学习笔记11,C++核心编程

目录

  • C++学习笔记11,C++核心编程
    • 对象的初始化和清理
      • 1、构造函数和析构函数
      • 2、构造函数的分类及调用
      • 3、拷贝构造函数调用时机
      • 4、构造函数调用规则
      • 5、深拷贝与浅拷贝
      • 6、初始化列表
      • 7、类对象作为类成员
      • 8、静态成员

对象的初始化和清理

对象的初始化和清理也是也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用,后果是未知的
使用完一个对象或者变量,没有及时清理,也会造成一定安全问题

C++利用构造函数和析构函数 解决上述问题,这两个函数会被编译器自动调用,完成对象的初始化和清理工作
对象的初始化和清理工作是编译器强制要求做的事情,如果不提供构造函数和析构函数,编译器会自动调用编译器提供的构造函数和析构函数的空实现(空函数体)。

1、构造函数和析构函数

  1. 构造函数:创建对象时,为对象的成员属性赋值,由编译器自动调用,无需手动
  2. 析构函数:对象销毁前,系统自动调用,执行一些清理工作
构造函数:类名(){}
	1、没有返回值,不写void
	2、函数名与类名相同
	3、可以有参数,可以发生重载
	4、程序在调用对象时候自动调用构造,无需手动调用,而且只会调用一次


	析构函数:~类名(){}
	1、没有返回值,不写void
	2、函数名与类名相同,在类名前加 ~
	3、不可以有参数,不可以发生重载
	4、程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
class Baby
{
private:
	string b_Name;
	string b_Sex;
	int b_Age;
public:
	//构造函数
	Baby()
	{
		cout << "Baby类 构造函数的调用" << endl;
	}
	//析构函数
	~Baby()
	{
		cout << "Baby类 析构函数的调用" << endl;
	}
};
void test12()
{
	Baby b1;	//在栈区的数据,test12()执行完毕后,释放这个对象
}
int main20()
{
	//test12();	//能够看到调用构造和析构函数

	Baby b2;	//只能看到调用构造函数,主函数未运行结束,不会调用析构函数

	system("pause");
	return 0;
}

2、构造函数的分类及调用

两种分类方式

  1. 按参数分为:有参构造和无参构造(默认构造)
  2. 按类型分为:普通构造和拷贝构造

三种调用方式

  1. 括号法
  2. 显示法
  3. 隐式转换法
//分类
class Person2
{
public:
	int p_Age;

public:
	Person2()
	{
		cout << "Person 的无参构造函数的调用" << endl;
	}
	~Person2()
	{
		cout << "Person 的析构函数的调用" << endl;

	}
	//有参构造函数
	Person2(int a)
	{
		p_Age = a;	//初始化
		cout << "Person 的有参构造函数的调用" << endl;
	}

	//拷贝构造函数	
	Person2(const Person2& p)	//加const保证本体不会被修改掉,同时必须引用传值
	{
		p_Age = p.p_Age;	//将传入的人身上的所有属性,拷贝到我身上
		cout << "Person 的拷贝构造函数的调用" << endl;

	}
};
//调用
void test13()
{
	//1、括号法
	//Person2 p1;		//默认构造函数的调用
	//Person2 p2(18);	//有参构造函数的调用
	//Person2 p3(p2);	//拷贝构造函数的调用
	//cout << "p2的年龄是:" << p2.p_Age << endl;
	//cout << "p3的年龄是:" << p3.p_Age << endl;
	/*
	注意事项
		1、调用默认构造函数的时候,不要加括号()
		因为编译器会认为	Person2 p1();	是一个函数的声明,不会认为在创建对象
	*/
	//2、显示法
	Person2 p1;
	Person2 p2 = Person2(18);//有参构造
	Person2 p3 = Person2(p2);//拷贝构造
	//等号右侧的是匿名对象(Person2(18))		特点:当前行执行结束后,系统会立即回收掉匿名函数
	/*
	注意事项
		2、不要利用拷贝构造函数,初始化匿名对象
		因为编译器会认为	Person2 (p3); == Person2 p3;(对象的一个声明)
	*/
	//3、隐式转换法
	Person2 p4 = 18;//相当于写了	Person2 p4 = Person2(18);
	Person2 p5 = p4;
}
int main21()
{
	test13();

	system("pause");
	return 0;
}

3、拷贝构造函数调用时机

常见的三种情况

  1. 使用一个创建完成的对象来初始化一个新对象
  2. 值传递的方式给函数参数传值
  3. 以值的方式返回局部对象
class Person3
{
public:
	int p_age;

	Person3()
	{
		cout << "Person3 默认构造函数调用" << endl;
	}
	~Person3()
	{
		cout << "Person3 析构函数调用" << endl;
	}
	Person3(int age)
	{
		p_age = age;
		cout << "Person3 有参构造函数调用" << endl;
	}
	Person3(const Person3& p)
	{
		p_age = p.p_age;
		cout << "Person3 拷贝构造函数调用" << endl;
	}
};
//1、使用一个创建完成的对象来初始化一个新对象
void test14_1()
{
	Person3 p1(18);	//调用默认构造函数
	Person3 p2(p1);	//调用拷贝构造函数
	cout << "p1的年龄是:" << p1.p_age << endl;
	cout << "p2的年龄是:" << p2.p_age << endl;

}
//2、值传递的方式给函数参数传值
void doWork(Person3 p)//值传递本质:会拷贝一个临时的副本出来
{
	//形参p会按照实参p1拷贝一份数据出来
	//调用拷贝构造函数
}
void test14_2()
{
	Person3 p1;	//调用默认构造函数
	doWork(p1);
}
//3、以值的方式返回局部对象
Person3 doWork2()
{
	Person3 p1;	//调用默认构造函数
	return p1;	//调用拷贝构造函数	
				//因为返回的是值,不是引用,所以会根据p1拷贝一个新的对象p1'。返回给test14_3()中的p2
}
void test14_3()
{
	Person3 p2 = doWork2();
}
int main22()
{
	//test14_1();
	test14_3();

	system("pause");
	return 0;
}

4、构造函数调用规则

默认情况下,C++编译器至少会给一个类添加3个函数:

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数(对属性进行值拷贝)

调用规则:

  1. 如果用户定义有参构造函数,C++不再提供默认无参构造,但会提供默认拷贝构造
  2. 如果用户定义拷贝构造函数,C++不再提供其他普通构造函数(默认无参和有参构造)

5、深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是最常见的一个坑!!!

浅拷贝:简单的赋值拷贝操作(编译器自动生成的就是浅拷贝)
深拷贝:在堆区重新申请空间,进行拷贝操作
class Person4
{
public:
	int p_Age;
	int* p_Height;

	Person4()
	{
		cout << "Person4 默认构造函数调用" << endl;
	}
	~Person4()
	{
		//析构函数,一般用来将 堆区开辟的数据做释放操作
		if (p_Height != NULL)
		{
			delete p_Height;	//释放空间
			p_Height = NULL;	//
		}

		cout << "Person4 析构函数调用" << endl;
	}
	Person4(int age, int height)
	{
		p_Age = age;
		p_Height = new int(height);//将height开辟到堆区

		cout << "Person4 有参构造函数调用" << endl;
	}
	//自己实现拷贝构造函数,解决浅拷贝带来的问题
	Person4(const Person4& p)
	{
		/*p_Age = p.p_Age;	//系统自动生成的 拷贝构造函数会包含这两行代码
		p_Height = p.p_Height;*/

		//自己写:(深拷贝)
		p_Age = p.p_Age;
		p_Height = new int(*p.p_Height);
		cout << "Person4 拷贝构造函数调用" << endl;
	}
};
void test15()
{
	Person4 p1(18, 180);
	cout << "p1的年龄是:" << p1.p_Age << "\t p1的身高是:" << *p1.p_Height << endl;

	Person4 p2(p1);
	cout << "p2的年龄是:" << p2.p_Age << "\t p2的身高是:" << *p2.p_Height << endl;
}
// p1 p2存放在栈区,栈区先进后出,p2先出,先调用析构函数,释放堆区;p1后出,再次调用析构函数,释放堆区
// 堆区被重复释放!!!
int main23()
{
	test15();

	system("pause");
	return 0;
}

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

6、初始化列表

作用:C++提供了初始化列表语法,用来初始化属性
语法: 构造函数() : 属性1(值1),属性2(值2)…{}

class Person5
{
public:
	int p_A;
	int p_B;
	int p_C;

	//传统初始化操作
	//Person5(int a, int b, int c)
	//{
	//	p_A = a;
	//	p_B = b;
	//	p_C = c;
	//}

	//初始化列表初始化操作
	//Person5() :p_A(10), p_B(20), p_C(30)
	//{

	//}

	//或者:
	Person5(int a, int b, int c) :p_A(a), p_B(b), p_C(c)
	{

	}

};
void test16()
{
	//Person5 p1(10, 20, 30);
	//cout << "a = " << p1.p_A << "\t " << "b = " << p1.p_B << "\t " << "c = " << p1.p_C << endl;
	
	//Person5 p2;
	//cout << "a = " << p2.p_A << "\t " << "b = " << p2.p_B << "\t " << "c = " << p2.p_C << endl;
	
	Person5 p3(40, 50, 60);
	cout << "a = " << p3.p_A << "\t " << "b = " << p3.p_B << "\t " << "c = " << p3.p_C << endl;
}
int main24()
{
	test16();

	system("pause");
	return 0;
}

7、类对象作为类成员

//C++类中的成员可以是另一个类的对象,称该成员为 对象成员

class A
{

};
class B
{
	A a;
};
//B类中有对象a 作为成员,称 a 为对象成员

class Phone
{
public:
	string PhoneName;

	Phone(string phonename) :PhoneName(phonename)
	{
		cout << "Phone 的构造函数的调用" << endl;
	}
	~Phone()
	{
		cout << "Phone 的析构函数的调用" << endl;
	}
};
class Person6
{
public:
	string p_Name;
	Phone p_PName;

	Person6(string name, string pname) :p_Name(name), p_PName(pname)
	{												//p_PName(pname) 系统会自动转换成:Phone p_PName = pnamel;
													//隐式转换法,即:Phone p_PName = Phone(pname);
		cout << "Person 的构造函数的调用" << endl;
	}
	~Person6()
	{
		cout << "Person 的析构函数的调用" << endl;
	}
};
void test17()
{
	Person6 p1 = Person6("张三", "华为P40");
	cout << p1.p_Name << "拿着" << p1.p_PName.PhoneName << endl;
}
int main25()
{
	test17();

	system("pause");
	return 0;
}

当其他类对象作为本类成员,构造时候先构造类对象成员,再构造自身;析构顺序与构造相反

8、静态成员

静态成员:在成员变量和成员函数前加上关键字 static,称为静态成员

分类
静态成员变量

	1、所有对象共享同一份数据
	2、在编译阶段分配内存
	3、类内声明、类外初始化

静态成员函数

	1、所有对象共享同一个函数
	2、静态成员函数只能访问静态成员变量
class Person7
{
public:
	string p_Name;
	static int p_Age;	//静态成员变量(类内声明)

	static void AAA()	//静态成员函数
	{
		cout << "static void AAA() 的调用" << endl;
		p_Age = 20;		  //静态成员函数	可以访问  静态成员变量
		//p_Name = "张三";//静态成员函数不可以访问非静态成员变量	无法区分是哪个对象的属性
	}

	//静态成员函数也有访问权限
//private:
//	static void BBB()
//	{
//		cout << "static void BBB() 的调用" << endl;
//	}
};
int Person7::p_Age = 18;//类外初始化(int不能省略
//static int p_Age = 18;	
//int p_Age = 18;		//都不对,必须声明是什么类型的哪个类下的静态变量
void test18()
{
	//两种访问方式

	//1、通过对象访问
	Person7 p1;
	p1.AAA();
	//2、通过类名访问
	Person7::AAA();

	//Person7::BBB();//私有权限  不可访问
}
int main26()
{
	test18();

	system("pause");
	return 0;
}

(哔哩哔 哩黑马程序员 C++教程 学习笔记,如有侵权请联系删除)


本文链接: http://www.dtmao.cc/news_show_700395.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?