C++类和对象 -封装


引用

基本使用

**作用: **给变量起别名
语法: 数据类型 &别名 = 原名

int a = 10;
int &b = a;

注意事项

  • 引用必须初始化
  • 引用在初始化后,不可以改变
int &a;	//这是错误的,因为必须初始化
int &c = a; //一旦初始化后,就不可以更改
c = b; //这是赋值操作,不是更改引用

引用作为函数参数

**作用:**函数传参时,可以利用引用的技术让形参修饰实参
**优点:**可以简化指针修改实参

void func(int &a, int &b){
   
	int tmp = a + b;
}

**注意:**在函数参数中的引用不可以传入一个临时变量,如果真的想引用这个变量,用const来保证我们不会对临时变量的内存进行更改。
示例:

//这里重载了左移运算符和递减运算符
class myInteger{
   
	friend ostream& operator<<(ostream &cout, const myInteger &i);
	
	public:
		myInteger(){
   
			this->mNum = 0;
		}
		
		myInteger& operator--(){
   
			this->mNum--;
			return *this;
		}
		
		myInteger operator--(int){
   
			myInteger tmp = *this;
			this->mNum--;
			return tmp;
		}
		
	private:
		int mNum;
};

//由于后置递减运算符返回的是一个临时变量,
//所以左移的引用必须用const修饰
ostream& operator<<(ostream &cout, const myInteger &i){
   
	cout << i.mNum;
	return cout;
}

int main(){
   
	myInteger m;
	cout << m-- << ' ' << m << endl;
}

引用做函数返回值

注意:不要返回局部变量引用

//返回局部变量引用
int& test01() {
   
	int a = 10; //局部变量
	return a;
}
//不能返回局部变量的引用,
//局部变量释放后编译器只回对其保留一次,之后就是乱码

引用的本质

本质:引用的本质在c++内部实现是一个指针常量. `int* const ref = &a;

int main(){
   
	int a = 10;    
    //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
	int& ref = a; 
	ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
}

常量引用

作用: 常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

int main() {
   
	//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
	//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
	const int& ref = 10;
}

构造函数与析构函数

  • 构造函数:用于初始化对象。
    • 语法:Name(){};
    • 没有类型,但是可以有参数,所以可以重构。
      • 系统默认给一个空的默认构造函数。
      • 可以用参数进行重构,只要参数数量、类型、顺序不一即可。
    • 程序在对象申请时自动调用,且只调一次。
  • 析构函数:用于释放初始化时申请的堆区内存。
    • 语法:~Name(){};
    • 没有类型也没有参数,故不可重构。
      • 系统也是默认给个空的函数。
    • 程序在对象销毁之前自动调用,且只调一次。

注意:

  • 函数写在public里。

构造函数的类型及调用

两种分类方式:

  • 按参数:有参构造和无参构造。
  • 按类型:普通构造和拷贝构造。

三种调用方式:

  • 括号法、显式调用、隐式调用。

拷贝构造函数的写法

Name(const Name &p){
   
	// 对一些值进行拷贝
}

三种调用方式

  • 括号法
    • Name p1(10);
    • 直接在创建的对象后面打括号写上参数,但是执行默认构造时不能打括号,因为Name p()会被认为是一种函数。
  • 显示调用
    • Name p2 = Name(p1);
    • 单独的Name(p1)会被视作给一个匿名对象进行构造,在这句语句执行完之后立即执行析构。
  • 隐式调用
    • Name p3 = 10;

注意:

  • 不可以用 拷贝构造函数 初始化匿名对象,编译器会认为这是一个对象的声明。
    • Name (p3) == Name p3;

拷贝构造函数调用的时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
    •   Name p(100);
        Name p1 = p;
      
        Name p3;
        p3 = p;	// 不是调用拷贝函数,而是赋值
      
  • 值传递的方式给函数参数传值
    •   void doWork(Name p1){
             }
        void main(){
             
        	Name p;
        	doWork(p);
        }
      
    • 此处其实是调用拷贝函数复制了一个类过去,而并非原来那个。
  • 以值方式返回局部对象
    •   Name doWork(){
             
        	Name p;
        	return p;
        }
      
    • 此时,因为p是局部对象,所以在函数执行完之后就销毁了,于是编译器用拷贝函数复制了一个新对象返回。

构造函数的调用规则

默认情况下,编译器提供给每个类三个函数:

  • 默认构造函数
  • 默认析构函数
  • 默认拷贝函数

调用规则:

  • 你写了构造函数,默认提供你析构和拷贝函数。
  • 你写了析构,提供拷贝,不提供构造。
  • 你写了拷贝,其他两个都不提供。

深拷贝与浅拷贝

  • 浅拷贝:简单的赋值拷贝操作。
  • 深拷贝:在堆区重新申请空间,进行拷贝操作。

简单来说,对于一些数值来说,我们直接给对应属性拷贝过去是可以的。但是对于一些地址来说,只拷贝地址数值是不行的,因为对于这两个对象的释放来说,这两个属性所指向的地址是相同的,所以会造成二次释放的问题,所以我们就需要在拷贝的时候在堆区申请一个新空间。

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


初始化列表

class Person{
   
public:
	//传统方式初始化
	/*Person(int a, int b, int c) {
		m_A = a;
		m_B = b;
		m_C = c;
	}*/

	//初始化列表
	Person(int a, int b, int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值