C++面向对象-封装上(复习)

面对对象是由c的面向过程所进化的,总体就三点
封装 继承 多态

一、理解

封装⼜叫隐藏实现,只公开代码单元的对外接口,⽽隐藏其具体实现。
封装思想的语法⽀持:
把 变量(属性) 和 函数(⾏为) 放在⼀起,作为⼀个整体
class封装了⼀个具体事物模型,通过这个模⼦我们可以⽣产多个具有相同特征的对象

二、成员访问控制

 -  public,类外可以访问。
 -  private,类外禁⽌访问。
 -  protected(继承)
类的访问控制并不是绝对的,因为引入了友元的概念。
友元可以访问类的私有成员,使用friend关键字。

友元函数
注意

 -  friend关键字只在声明处出现。
 - 友元函数不是类的成员函数,不包含 this指针。
 -  友元函数可以访问对象的任意私有成员。

友元类以及友元成员函数

 - 友元类中的所有函数都可以访问本类型的私有成员。  友元关系不能被继承。
 - 友元关系是单向的,类 A是类 B的朋友,但类 B不⼀定是类 A的朋友。
 - 友元关系不具有传递性。类B是类 A的朋友,类 C是类 B的朋友,但类  C不⼀定是类 A的朋友
 - 友元成员函数就是在A类里将B类的某个成员函数设置为友元

三、运算符重载

这里只说一下运算符重载比较重要的几个
 - ⽐较运算符的结算结果⼀般是truefalsebool类型,当然也可以返回01 代表的C⻛格的bool类型。
 - 转换运算符:当我们希望⾃定义类能够转换为其他数据类型时,也可以重载相关的转换运算符。例如,将  Box类型 的对象转换为int类型、bool类型或者其他⾃定义类型
1.转换运算符不能写返回值类型。
2.转换运算符不能有参数。
1. 左移运算符重载是为了让 cout对象输出⾃定义类型。
i. 由于 ostream类内部删除了拷⻉构造函数,不能以值⽅式传参、返回
ii. 由于内建的 operator<<重载函数⾮  const函数,所以  ostream参数不能⽤  const修饰。
2. 右移运算符重载是为了让 cin运算符能够向⾃定义类型写⼊。
i. 由于 istream类内部删除了拷⻉构造函数,不能以值⽅式传参、返回
赋值运算符注意⼏个问题:
1. 赋值操作和初始化操作的区别。
2. 浅赋值操作和深赋值操作(类似深拷⻉和浅拷⻉区别)3. 如果没有重载赋值运算符,进⾏对象赋值时,编译器会进⾏简单值拷⻉。
4. 为了实现 box1 =box2 = box3  连续赋值,所以赋值运算符必须返回对象引⽤。

四、 构造函数及析构函数

构造函数和析构函数,这两个函数将会被编译器⾃动调⽤,完成对象初始化和对象
清理⼯作。即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。

class Person{
public:
//  构造函数
Person(){
	cout  <<  "构造函数"  <<  endl;
	//  对成员变量进⾏初始赋值
	m_name  =  new  char[32];
	memset(m_name,  0,  32);
	strcpy(m_name,  "Obama");
	m_age  =  33;
}
//  普通成员函数
void  print(){
	cout  <<  "姓名:"  <<  m_name  <<   " 年龄:"  <<  m_age  <<  endl;
}
//  析构函数
~Person(){
	cout  <<  "析构函数"  <<  endl;
	if  (m_name  !=  NULL)
	{
	free(m_name);
	}
	}
	private:
	char  *m_name;
	int  m_age;
};
void  test(){
	Person  person;
	person.print();
}

构造函数分类

因为构造函数支持重载
1.  普通构造函数,使⽤基本数据类型参数初始化对象。
2.  拷⻉构造函数,使⽤⼀个同类型的对象初始化本对象。
3.  如果提供了任何构造函数,默认构造函数需要⼿动添加。
class  Person{
public:
	//  普通构造函数
	Person()
	{
	cout  <<  "⽆参构造函数"  <<  endl;
	m_score  =  0;
	m_age  =  0;
}
Person(int  score){
	cout  <<  "⼀个参数构造函数"  <<  endl;
	m_score  =  score;
	m_age  =  0;
}
Person(int  score,  int  age){
	cout  <<  "两个参数构造函数"  <<  endl;
	m_score  =  score;
	m_age  =  age;
}
//  拷⻉构造函数
Person(const  Person  &person){
	cout  <<  "拷⻉构造函数"  <<  endl;
	m_score  =  person.m_score;
	m_age  =  person.m_age;
}
void  print(){
	cout  <<  m_age  <<  "  "  <<   m_score  <<  endl;
}
public:
	int  m_age;
	int  m_score;
};
//  1.  ⽆参构造函数调⽤
void  test01(){
	//  正确写法
	Person  person;
	//  错误写法
	//  Person  person();
	//  person.print();
	}
//  2.  调⽤有参构造函数
void  test02(){
	//  1.  括号法
	// 调⽤1个参数构造函数
	Person  person1(10);
	// 调⽤2个参数构造函数
	Person  person2(10,  20);
	//  调⽤拷⻉构造函数
	Person  person3(person2);
	//  2.  =号法(对于⼀个参数构造函数、或者包含默认参数的构造函数 )
	Person  person4  =  10;  //  Person   person4(10);
	Person  person5  =  person4;    //   Person  person5(person4);
}
//  3.  匿名对象
void  test03()
{
	//  创建匿名对象,声明周期仅限于本⾏
	Person(10);
	Person(10,  20);
	// 错误写法(重定义)
	Person  person;
	//  Person(person);
	//  Person  person  =  Person(person);
}

五、拷贝构造函数

1. 对象拷⻉(复制)语意
i.  什么都不做,默认的逐字节拷⻉。但当类包含对象成员时,不会进⾏逐字节拷⻉
ii.  禁⽌对象拷⻉,将拷⻉对象私有
2.  深拷⻉和浅拷⻉问题
3.  拷⻉够构造函数调⽤时机
i.  对象以值传递的⽅式传给函数参数
ii.  对象以值传递的⽅式从函数返回
iii.  ⽤⼀个对象初始化另⼀个对象
4.  NRV具名返回值优化

1.对象拷贝

默认拷贝
class  Person{
	public:
	Person(int  a,  int  b)
	{
	m_a  =  a;
	m_b  =  b;
}
#if  0
	Person(const  Person  &)  =  delete;
#else
	private:
		Person(const  Person  &)  {}
#endif
public:
	int  m_a;
	int  m_b;
};
void  test(){
	Person  person1(10,  20);
	Person  person2(30,  40);
	//  默认逐字节拷⻉
	person1  =  person2;
}

调⽤对象成员拷⻉构造函数

class  Weapon{
	public:
		Weapon(){
		cout  <<  "Weapon 构造函数"  <<  endl;
		}
		Weapon(const  Weapon  &){
		cout  <<  "Weapon 拷⻉构造函数"  <<   endl;
		}
};
class  Person{
	public:
		Person(int  a){
		m_a  =  a;
	public:
		int  m_a;
		Weapon  m_weapon;
};

禁止对象拷贝

class  Person{
#if  0
	Person(const  Person  &)  =  delete;
#else
	private:
		Person(const  Person  &)  {}
#endif
};

2.深拷贝和浅拷贝
默认构造函数进⾏的逐字节拷⻉Bitwise Copy ⼤部分情况下是⼯作良好,但类中包含了指向动态对象的指针,默认的拷⻉⾏为就会带来很严重的后果。

class  Person{
	public:
	Person(const  char  *name){
		m_name  =  new  char[strlen(name)  +   1];
		strcpy(m_name,  name);
}
~Person(){
	if  (m_name  !=  NULL){
		delete[]  m_name;
	}
}
	public:
		char  *m_name;
};
void  test(){
	Person  person("Obama");
	Person  copy_person(person);
}

程序运行失败,如果要解决问题需要添加一个拷贝构造函数

Person(const  Person  &person){
		m_name  =  new  char[strlen(person.m_name)   +  1];
		strcpy(m_name,  person.m_name);
}

3拷贝构造调用时机

1.  对象以值传递的⽅式传给函数参数
2.  对象以值传递的⽅式从函数返回
3.  ⽤⼀个对象初始化另⼀个对象
class  Person  {
	public:
		Person(){
			cout  <<  "no  param  contructor!"   <<  endl;
			mAge  =  10;
}
Person(int  age){
		cout  <<  "param  constructor!"  <<   endl;
		mAge  =  age;
}
Person(const  Person&  person){
		cout  <<  "copy  constructor!"  <<   endl;
		mAge  =  person.mAge;
}
~Person(){
		cout  <<  "destructor!"  <<  endl;
}
public:
	int  mAge;
	};
	//1.  旧对象初始化新对象
void  test01(){
	Person  p(10);
	Person  p1(p);
	Person  p3  =  p;
	}
	//2.  传递的参数是普通对象,函数参数也是普通对象,传递将会调⽤拷⻉构造
void  doBussiness(Person  p)  {}
void  test02()
{
	Person  p(10);
	doBussiness(p);
}
//3.  函数返回局部对象
Person  MyBusiness(){
	Person  p(10);
	cout  <<  "局部p:"  <<  (int*)&p   <<  endl;
	return  p;
}
void  test03(){
	Person  p  =  MyBusiness();
	cout  <<  "局部p:"  <<  (int*)&p   <<  endl;
}

具名返回值优化

class  Teacher{
	public:
	Teacher()  {}
	Teacher(const  Teacher  &teacher){
		cout  <<  "拷⻉构造函数"  <<  endl;
		for  (int  i  =  0;  i  <  100;   ++i){
			m_score[i]  =  teacher.m_score[i];
			}
	}
	private:
		int  m_score[100];};
		Teacher  foo(){
			Teacher  teacher;
			return  teacher;
}
void  test(){
	for  (int  i  =  0;  i  <  10000000;   ++i){
		Teacher  t  =  foo();}
}

test函数调⽤了 foo函数 1000万次,每⼀次都会进⾏对象拷⻉,效率太低。为了解决这种问题,编译器进⾏了代码优化,使得函数返回局部对象时不再调⽤拷⻉构造,这种优化技术叫做NRV,NamedReturn Value

void  foo(Teacher  &_result){
// 调⽤  _result  的构造函数
	_result.X::X();
// 对  _result  进⾏的操作
//....
	return;
}
void  test(){
	for  (int  i  =  0;  i  <  10000000;   ++i){
		Teacher  t;  //  只分配内存,不能调⽤构造函数初始化
		foo(t);}
}

构造函数规则

**1.程序员⻆度**
-默认情况下,C++编译器会根据是否需要为类增加构函数   1.默认构造函数(⽆参,函数体为空)
2.默认析构函数(⽆参,函数体为空) 3.默认拷⻉构造函数,对类中⾮静态成员属性简单值拷⻉
- 如果⽤户定义拷⻉构造函数,C++不会再提供任何默认构造函数
-如果⽤户定义了普通构造(⾮拷⻉),C++不再提供默认⽆参构造,但是会提供默认拷⻉构造
**2.编译器⻆度**
-  编译器只有在需要的情况下才会为类增加默认构造函数,其中⼀个场景就是类内部包含对象成
员。
-  编译器只有在需要的情况下才会为类增加默认析构函数,其中⼀个场景就是类内部包含对象成
员,并且对象成员包含析构函数。
- 当⼀个类内部没有提供拷⻉构造函数时,编译器只有在需要的情况下才会增加拷⻉构造函数,如
果包含对象成员,那么会增加默认拷⻉构造函数。C++编译器默认对内建的数据类型进⾏逐字节
拷⻉。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值