C++ (二)

1.C++的动态内存

new/delete
new[]/delete[]
操作符   
new和malloc的区别?

2.C++的引用

引用的底层实现其实就是指针
引用必须初始化 一经初始化不能更改目标  引用不能为空
引用提高传参效率,节省内省,比指针操作简单  
引用与指针的区别?

3.C++的类型转换

隐式类型转换(自动)
强制类型转换
显示类型转换:
	static_cast<>()
	const_cast<>()
	reinterpret_cast<>()
	dynamic_cast<>()

4.C++面向对象

类: 一为事物的抽象描述  泛化的概念  泛指
对象: 类实例化具体化的结果  特指
抽象: 把事物共同的特征抽象为属性 把事物共同的行为抽象为方法
封装: 把一类事物用类的语言来描述,加以访问控制属性的限制
	public\protected\private
类的语法:
class 类名{
	//成员属性
	//成员方法
	//构造方法 函数
	//没有返回值类型(也不能是void) 函数名和类名一样 可以重载 有初始化列表 
	//如果一个类没有提供构造方法,那么会自动添加一个无参构造方法 一旦显式提供则不再提供默认的无参构造
	//初始化列表的执行顺序只与成员属性的声明顺序有关
	类名(形参列表):初始化列表{
		
	}
	//成员方法中有一个隐含的this指针  指向正在调用或者构造的那个对象
	//当成员变量与局部变量同时名  可以用  this->  来访问成员变量
	//常方法
	返回值类型  方法名(形参列表)const{
		//const修饰的是 this 指针所指向的对象
	}
};
对象的实例化
	类名(实参列表);   类名  变量名(实参列表);
	类名  变量名;
	类名  变量名();  //不是实例化对象 而是声明一个函数
	
	new 类名;
	new 类名();
	new 类名(实参列表);
常对象: 具有常属性的对象const
	常对象只能调用常方法(不能调用非常成员方法)
	
	非常对象 可以  自动提升为 常对象 
	但是常对象的 const 属性 不能随便去掉
	
常版本与非常版本的 成员方法 构成重载
非常对象如果在调用时 有 非常版本的函数 调用 非常版本函数  如果没有则也可以调用常版本的函数 
对于重载:
	参数列表不同:
		参数类型不同: 指针和引用的常属性不同也构成重载
		
常成员方法中不能对成员属性进行修改  const *this
	但是如果用mutable关键字修饰的成员属性 就可以被修改
静态属性(类属性) 和 静态方法(类方法)
	属性类 而不属于某个对象
	所有该类的对象共同拥有一份  类属性存储位置和普通属性存储的位置不一样
	静态属性必须在类外声明初始化
	静态方法中没有隐含的this指针  所以在静态方法中不能访问成员属性和调用成员方法
	静态方法中只能访问静态属性和调用静态方法
	
	静态属性和静态方法也可以通过普通对象来访问和调用
	静态属性和静态方法相当于是加了类作用域的全局变量和函数 
单参构造:
	如果一个类有单参构造函数
	A(B b){}
	那么任何B类型的对象都可以自动(调用该构造函数)转换为A类型对象
	如果要禁止自动(必须明确调用构造函数   A(b)) 则在构造方法前加  explicit
必须使用初始化列表的情况:
	1.常属性成员变量
	2.引用型成员变量
	3.没有无参构造的类类型的成员变量
	
构造对象时,先按照声明顺序去构造 成员变量,即调用每个对象所属类的无参构造函数 最后执行本类的构造函数体
析构时 正好和构造的顺序相反
5.析构函数
一个类有一个默认的无参构造函数 该函数没有实现任何功能
当一个对象消亡或者 delete时 就会调用该构造函数
所以当一个对象在构造时如果new的动态内存,那么就需要自己手动实现析构函数 
在析构函数中释放之前new申请的动态内存
析构函数:  没有返回值类型  也不是void
	~类名(){
		
	}
	析构函数不能重载   因为析构无参
6.拷贝构造函数(深拷贝 、 浅拷贝)
用一个已经存在的对象来构造一个新的同类型的对象  调用的就是拷贝构造函数
所谓拷贝构造 本质 就是复制克隆一个新对象

编译器会自动给类提供一个拷贝构造函数
拷贝构造函数形如:
类名(const 类名& other){
	
}
	默认的拷贝构造函数实现的方式是按字节拷贝(memcpy)  构造的新对象 和 旧对象是完全一模一样
	按字节拷贝持方式是浅拷贝
	深拷贝:
		拷贝指针所指向内存地址中的数据 而不是拷贝指针的值本身
		之间旧的C++版本对同一块动态内存多次delete会出错  
如果一个类需要实现深拷贝的功能 则需要实现拷贝构造函数

用一个旧的对象来构造一个新的对象
类名 对象 = 旧对象;
类名 对象(旧对象);
void func(类名 obj){}
func(对象);


旧对象 = 旧对象;   两个对象都已经存在了,则不会调用拷贝构造
对象之间可以用 = 进行相互赋值

默认的赋值是按字节拷贝的形式进行赋值 
	如果是指针 则复制指针的值  并不是把指针所指向内存中的数据进行复制
7.拷贝赋值函数
如果没有提供拷贝赋值函数 则编译器会自动提供拷贝赋值函数 
拷贝赋值函数默认实现也是按字节复制(浅拷贝)
如果有需要实现深拷贝,则需要自己实现
形如:
类名& operator=(const 类名& other){
	
}
8.string
    class string{
    private:
	char *s;
  public:
	string(const char *str);
	~string();
	string(const string& str);
	string& operator=(cosnt string& str);
};
8.默认的无参构造 拷贝构造 拷贝赋值函数
默认无参 调用 成员的无参构造
	如果需要调用指定的有参 需要在初始化列表
拷贝构造 调用 成员的拷贝构造 进行 构造
拷贝赋值  调用 成员的拷贝赋值函数 进行 赋值

在实现拷贝构造和拷贝赋值函数的时候,类类型成员需要手动调用拷贝构造 和 拷贝赋赋值
class string{
private:
	char *str;
public:
	string(const char* s=""):str(strcpy(new char[s==NULL?1:strlen(s)+1],s==NULL?"":s)){
		/*
		size_t len = s==NULL?1:strlen(s)+1;
		str = new char[len];
		strcpy(str,s==NULL?"":s); 
		*/
	}
	string(const string& s):str(strcpy(new char[strlen(s.str)+1],s.str)){
		/*
		str = new char[strlen(s.str)+1];
		strcpy(str,s.str);
		*/
	}
	string& operator=(const string& s){
		if(this != &s){
			string stmp(s);
			swap(str,stmp.str);
		}
		return *this;
	}
	~string(){
		if(str != NULL){
			delete [] str;
			str = NULL;
		}
	}
};
9.成员属性指针 成员函数指针
定义成员属性指针变量
属性类型 类名::*指针变量;

初始化 or 赋值
属性类型 类名::*指针变量 = &类名::属性名;  //不是一个真实的地址
指针变量 = &类名::属性名;

成员指针直接解引用    对象
	对象.*成员指针 
成员指针间接解引用    对象的指针
	对象的指针->*成员指针


定义成员函数指针变量
成员函数返回值  (类名::*指针)(成员函数形参列表);

初始化 or 赋值
成员函数返回值  (类名::*指针)(成员函数形参列表) = &类名::成员函数名;
指针 = &类名::成员函数名;

成员函数指针直接解引用  对象
	(对象.*成员函数指针)(实参列表);

成员函数指针间接解引用  对象的指针
	(对象的指针->*成员函数指针)(实参列表);

2. friend

在一个类里面 可以声明另外的类为该类的友元类  
在友元类中可以访问该类对象的私有属性和私有函数
也可以声明一个全局函数为该类的友元函数  则在友元函数中也可以访问该对象的私有内容
友元的声明是单向性的

3.运算符重载

在对应的class中提供对应的 运算符函数 来实现功能
本质上是一个函数
返回值类型 operatorX(形参列表){
	
}

拷贝赋值:   重载=运算符
	

重载输入输出运算符:
	只能以友元函数的形式重载
//重载输出运算符
friend ostream& operator<<(ostream& os,const 类名& 对象){
	
}
//重载输入运算符
friend istream& operator>>(istream& is,类名& 对象){
	
}


对运算符进行分类 按操作数的个:
	1.单目运算符    +(正) -(负)  *(取值)  &(取地址)  !(逻辑非)   ~(按位取反)  ++  --
		#obj;  --->  obj.operator#()
	2.双目运算符    
		<<  >>      两个操作数对象的类型不一样
		cout << n;    cout n
		cin >> n; 
		o1 # o2    o1[o2]   -->  o1.operator(o2)
		cout.operator<<(stu)  ostream类型早已封装好了  不能给ostream类重载这样一个成员函数
		<< 和 >> 运算符 只能以全局变量的形式进行重载
	3.三目运算符  ?:     三目运算符不可以进行重载


以成员的方式重载运算符 第一个操作对象隐身为*this
	单目运算符以成员方式重载 没有 参数
	双目运算符以成员方式重载 只有一个参数
	

写个Complex类
	实现 + - * /  <<  >>
		+  -  以友元方式重载 
		*  /  以成员方式重载
		-     重载负数运算符
		~     调换实部和虚部

++  -- 运算符重载
	用哑元来区分前++--和后++--
	类名& operator++(){}
	const 类名 operator++(int){}
	
[]  一般来说都会实现一个常版本 和一个非常版本

()  重载()运算符的类的对象 称为 函数对象  因为这种类型的对象 可以像函数一样调用
	C语言中用函数指针来实现回调
	C++中用函数对象来实现回调
	
A类中有B作为参数的单参构造函数   B类型对象---> A类型的对象 
class A{
public:
	A(B& b){}
};

重载类型运算符:
class A{
	operator B(){
		return B类型对象;
	}
}
	A类型对象都可以自动转换成B类型的对象
	
注意: A 类型对象怎么可以自动转换为 B类型的对象?   A->B
	1. 在B类中添加A类型的单参构造
	2. 在A类中重载B类型的类型运算符

new/delete    new[]/delete[]
	可以以全局函数的方式重载
	也可以以类的静态方式重载
	
注意:  == 和 != 成对出现
	排序 查找 映射   需要重载 <  

1.不能重载的运算符
	.   ::  sizeof   typeid   .*  
2.只能以友元方式重载
	<<   >>
3.只能以成员方式重载
	=   []   ()
4.不能创建新的运算符
	x**y
5.运算符重载应该保持其原有的含义和运算规则 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值