一、常函数

1.1、定义

普通函数: 
	返回类型 函数名(形参表){函数体}
常函数:
	返回类型 函数名(形参表)const{函数体}
const 关键字放在函数声明的后面
或者在形参表和函数体之间添加一个 const 关键字,这样的函数称之为常函数。
    
1. 常成员函数无法改变成员变量的值
2. 并且无法调用其它可以改变成员变量的成员函数
3. 无法修改成员变量的值,本质上是在此函数的内部的 this 指针前加了 const 关键字

1.2、常函数中的 const 关键字是用来修饰this指针

void show(){}
// 可以看成 void show(类名* this){}
void show()const{}
// 可以看成 void show(const 类名* this){}
void print(){}
// 可以看成 void print(类名* this){}
	
作用:不可以在常函数中修改类中的成员。
a.show(&a)
a.print(&a)
//参考案例:03constfunc.cpp
#include <iostream>
using namespace std;
class Integer{
public:
	Integer(int a):m_a(a){}
	void setValue(int a){
		m_a = a;
	}
	// 常函数
	void show(void)const{
		// m_a++; 常函数中不可以修改类中成员
		cout << m_a << endl;
	}
private:
	int m_a;
};
int main(int argc, const char *argv[])
{
	Integer i1(100);
	i1.setValue(200);
	i1.show();
	return 0;
}
//结果:
200

1.3、mutable 强制在常函数中修改类中的成员 ,

在修改的类中的成员前边加 mutable 关键字即可
//参考案例:04constfunc.cpp
#include <iostream>
using namespace std;
class Integer{
public:
	Integer(int a):m_a(a){}
	void setValue(int a){
		m_a = a;
	}
	// 常函数
	void show(void)const{
		m_a++;
		// 使用mutable修饰的成员可以在常函数中修改
		cout << m_a << endl;
	}
private:
	mutable int m_a;
};
int main(int argc, const char *argv[])
{
	Integer i1(100);
	i1.setValue(200);
	i1.show();
	return 0;
}
//结果:
201

1.4、常函数版本和非 常函数版本可以构成重载的关系。

常对象优先调用常函数版本,
常对象只能调用常函数版本。
	
非 常对象优先调用非 常函数版本。
如果没有非 常函数版本,才会调用常函数版本。
参考案例:05constfunc.cpp
#include <iostream>
using namespace std;
class Integer{
public:
	Integer(int a):m_a(a){}
	void setValue(int a){
		m_a = a;
	}
	// 常函数版本
	void show(void)const{
		cout << "常函数" << endl;
		// m_a++; 常函数中不可以修改类中成员
		cout << m_a << endl;
	}
	// 非常函数版本
	void show(void){
		cout << "非常函数" << endl;
		cout << m_a << endl;
	}
private:
	int m_a;
};
int main(int argc, const char *argv[])
{
	// 非 常对象
	Integer i1(100);
	i1.setValue(200);
	i1.show();

	// 常 对象
	const Integer i2(300);
	// 常对象只能调用常函数,不可以调用非常函数.
	//	i2.setValue(400);
	i2.show();
	return 0;
}
//结果:
非常函数
200
常函数
300
const int p = 100; 常量
const int *p = &a; 常量指针 
int const *p = &a; 常量指针 
int *const p = &a; 指针常量

二、静态成员

定义:在类中使用static修饰的成员,称为静态成员

2.1、静态成员变量

语法格式: 
class 类名{
	// 声明一个静态的成员变量
	static 数据类型 变量名;
};
// 类的外部定义和初始化 
数据类型 类名::变量名 = 初始值;
总结: 
1.静态成员不属于类,只是在类的内部进行声明,静态成员变量放在全局静态数据区,只开辟一次内存空间,不占用类的空间,且每个对象都可见。

2.对于类中的静态成员的定义和初始化必须在类的外部完成。
	格式: 
		数据类型 类名::变量名 = 初始值;
3. 对于静态成员的访问,静态成员的访问依然受访问控制属性的限制。
	类名 对象名;
	对象名.静态成员变量名;
		
	类名 *对象指针;
	对象指针 -> 静态成员变量名;
		
	不需要实例化对象可以通过"类名::"直接访问类中的静态成员。
	类名::静态成员变量名;
//参考案例:06staticVari.cpp 
#include <iostream>
using namespace std;
class A{
	public:
	//静态成员变量初始化不可以在构造函数中完成
	A(int a):m_a(a)/*,m_b(b)*/{}
	void show(){
		cout << "m_a = " << m_a << endl;
		cout << "m_b = " << m_b << endl;
	}
	private:
	int m_a;
	public:
	static int m_b; // 声明静态成员
};
int A::m_b = 200;

class B{
	void show()
	{
		cout << B::m_c << endl;
	}
	public:
		static int m_c;
};
// 类的外部进行定义和初始化
int B::m_c = 1111;

int main(int argc, const char *argv[])
{
	// 空类,编译器默认会分配1个字节的空间
	cout << sizeof(B) << endl;

	// 静态成员放在全局区,不属于类
	cout << sizeof(A) << endl;

	A a(100);
	a.show();
	cout << a.m_b << endl;
	cout << A::m_b << endl;

	cout << B::m_c << endl;
	return 0;
}
//结果:
1
4
m_a = 100
m_b = 200
200
200
1111

2.2、静态成员函数

语法格式: 
class 类名{
	// 声明一个静态成员函数 
	static 返回类型 函数名(形参表);
		
	// 声明一个静态的成员变量
	static 数据类型 变量名;
};
静态成员函数的定义: 
static 返回类型 类名::函数名(形参表){
	函数体;
}
1. 静态成员函数属于全局的函数,只是在类的内部使用 static 关键字进行修饰。
2. 静态成员函数没有 this 指针,因此静态成员函数不可以是常函数。
3. 静态成员函数的访问(静态成员函数一般为 public 属性)
	1> 对象的方式 
	对象名.函数名(实参表);
	对象指针->函数名(实参表);
	2> 类名:: 的方式 (不需要实例化对象可以直接访问)
	类名:: 函数名(实参表);
	静态成员函数也受访问控制属性的限制。
4. 静态成员函数只能访问静态成员变量。原因:静态成员函数没有this指针
5. 非 静态成员函数既能访问静态成员变量也可以访问非静态成员变量。
//参考案例:07staticfunc.cpp
#include <iostream>
using namespace std;
class Student{
public:
	Student(const string& name,int age):m_name(name),m_age(age){}
	~Student(void){}
	//静态成员函数
	static void setScore(int score){
		//只能访问静态成员
		m_score = score;
		//m_age = 20;
	}

	static void setScore(const string& name,int age, int score, Student* other)
	{
		other->m_name = name;
		other->m_age = age;
		m_score = score;
	}
	
	void show(void)const{
		cout << "name:" << m_name << endl;
		cout << "age:" << m_age << endl;
		cout << "score:" << m_score << endl;
	}
private:
	string m_name;
	int m_age;
	static int m_score;
};
int Student::m_score;

int main(int argc, const char *argv[])
{
	Student stu0("xiaokai", 19);
	Student stu1("xiaozhou", 18);
	stu1.setScore(88);
	stu0.show();
	stu1.show();
	
	Student::setScore(99);
	stu1.show();
	
	Student::setScore("xiaodai",16,100,&stu1);
	stu1.show();

	return 0;
}

//结果:
name:xiaokai
age:19
score:88
name:xiaozhou
age:18
score:88
name:xiaozhou
age:18
score:99
name:xiaodai
age:16
score:100

3.3、访问方法

所有对象都可以访问
也可以通过类名访问

3.4、静态成员函数的特点

只能访问静态成员变量,无法访问非静态的成员变量(为什么:没有this指针)
所有对象都可以访问并且也可以通过类名访问
必须是public访问权限

3.5 静态成员函数和普通成员函数

静态成员函数 普通成员函数
所有对象共享
隐含this指针
访问普通的成员变量
访问静态成员变量
通过类名访问
通过对象名访问

三、友元

3.1、友元函数

1> 定义:将一个全局的函数,在类的内部使用 friend 修饰的函数,这个函数称为这个类的友元函数。
格式: 
	class 类名{
		public: 
		// 友元函数的声明
		friend 返回类型 函数名(形参表);
	};	
2> 友元函数的实现只能在类的外部实现,友元函数的实现不需要加"类名::"
	返回类型 函数名(形参表)
	{
		函数体;
	}
3> 调用友元函数 
	函数名(实参表);
4> 友元函数的作用
	友元是一种关系
	友元函数打破了类中成员访问控制属性的限制,
	在友元函数中可以访问保护的、公有的、私有的成员
5> 友元函数不属于类,他是一个全局函数,友元函数没有 this 指针,不能是常函数
6> 友元函数访问类中的成员,对于友元函数的形参有要求,
	如果只访问类中的静态成员,友元函数不需要形参。
		原因:静态成员属于全局
	如果访问非静态的成员,友元函数需要一个类类型的形参。
		原因:没有this指针
//参考案例:08friendFunc.cpp
#include <iostream>
using namespace std;
class Hero{
public:
	Hero(const string& name,int hp):m_name(name),m_hp(hp){}
	friend void show(void);
	friend void show(Hero *other);
private:
	string m_name;
	int m_hp;  
	static int m_hurt;
};

int Hero::m_hurt = 2000;

void show(void)
{
	cout << Hero::m_hurt << endl;
}

void show(Hero *other)
{
	cout << other->m_name << endl;
	cout << other->m_hp << endl;
	cout << Hero::m_hurt << endl;
}

int main(int argc, const char *argv[])
{
	show();
	Hero h1("daji", 5000);
	
	show(&h1);
	return 0;
}
//结果:
2000
daji
5000
2000

3.2、友元类

1> 定义
	假设有两个类:类A 类B 
	类A 中可以将另外一个类 类B声明为自己的友元类,则类B中的所有的成员函数都将变成类A的友元函数,则类B中的成员函数可以访问类A中的所有的成员,不在受访问控制属性的限制。
	
class A{
public: 
	// 将类B声明为A的友元类
	friend class B; 
private:
	int m_a;
	 static int m_b;
};
class B{   // 将变成类A的友元类
public:
	A m_A;  // 成员子对象
	void show(){
		cout << m_A.m_a << endl;
		cout << A::m_b << endl
	}  // 变成类A的友元函数	
};
//参考案例:09friendClass.cpp
#include <iostream>
using namespace std;
class A{
public:
	A(int data2):m_data2(data2){}
	friend class B;
private:
	static int m_data1;
protected:
	int m_data2;
};
int A::m_data1 = 10010;
class B{
public:
	B(int a):m_A(a),m_c(a){}
	void show(){
		cout << A::m_data1 << endl;
		cout << m_A.m_data2 << endl;
	}
	void show(char){
		cout << m_A.m_data1 << endl;
		cout << m_A.m_data2 << endl;
	}
private:
	A m_A;
	int m_c;
};

int main(int argc, const char *argv[])
{
	B b(10011);
	b.show();
	b.show('a');
	return 0;
}
//结果:
10010
10011
10010
10011
访问控制属性: 
public: 公有的成员
	类的内部,类的外部,派生类中都可以直接访问。
	在友元函数中也可以访问。
protected: 保护的成员
	类的内部,和派生类中都可以访问,类的外部不可以访问。
	在友元函数中也可以访问。
private: 私有的成员
	类的内部可以访问,类的外部和派生类中都不可以访问。
	在友元函数中也可以访问。
    
友元是单向,不具备传递性
两个类可以互为友元类,只是在声明时要按照一定格式声明

四、操作符的重载

4.1、运算类的双目运算符的重载

+ + - * / % > < >= <= == !=
	
表达式: L # R 
L : 左操作数 --> 运算符左边的
R : 右操作数 --> 运算符右边的
# : 运算符 
//注意:运算符必须是 C++ 中定义的运算符
//运算符重载的意义:可以扩展运算符的功能
//运算符重载发生的位置:在类的内部
1> 左右操作数既可以是左值,也可以是右值,
	100 + 200
	int a, b;
	c = a + b 
	a = 100;
	b = 200;
	a = b; b = a;
2> 表达式的结果是一个右值
	a + b = 100; // error 
	
	+ - * / %  格式: 
	类名 operator#(类名& R){}
	
	> < >= <= == !=  格式:
	> bool operator#(类名& R){}
参考案例: 11Complex.cpp 
	实现两个复数相加,
	实现两个复数的比较。
#include <iostream>
using namespace std;
class Complex{
public:
	Complex(int r, int i):m_r(r),m_i(i){
		cout << "构造函数" << endl;	
	}
	~Complex(void){
		cout << "析构函数" << endl;
	}
	Complex(const Complex& other):m_r(other.m_r),m_i(other.m_i){
		cout << "拷贝构造函数" << endl;	
	}
	// '='
	Complex& operator=(const Complex& other){
		cout << "拷贝赋值函数" << endl;
		if(&other != this){
			m_r = other.m_r;
			m_i = other.m_i;
		}
		return *this;
	}
	/*
	 * 第一个const : 返回值是右值
	 * 第二个const : 不可以修改右操作数
	 * 第三个const : 不可以修改左操作数
	 * */
	// '+'
	const Complex operator+(const Complex& other)const{
		cout << "+运算符重载" << endl;
		       // 临时对象
		return Complex(m_r+other.m_r,m_i+other.m_i);
	}
	// '>'
	const bool operator>(const Complex& other)const{
		return (m_r>other.m_r)?true:false; 
	}
	// '+='
	Complex& operator+=(const Complex& other){
		m_r += other.m_r;
		m_i += other.m_i;
		return *this;
	}
	void show()
	{
		cout << m_r << "+" << m_i << "i" << endl;
	}
private:
	int m_r;
	int m_i;
};


int main(int argc, const char *argv[])
{
	// c1和c2对象都是左值
	Complex c1(1,2);
	Complex c2(3,4);
	
    // c1.operator+(c2)
	Complex c3 = c1 + c2;
	c3.show();
	cout << boolalpha << (c1 > c2) << endl;

	// c4, c5对象为右值
	const Complex c4(5,6);
	const Complex c5(7,8);
	Complex c6 = c4 + c5;
	c6.show();

	cout << (c5 > c4) << endl;

	c3 += c6;
	c3.show();


	return 0;
}
//结果:
构造函数
构造函数
+运算符重载
构造函数
4+6i
false
构造函数
构造函数
+运算符重载
构造函数
12+14i
true
16+20i
析构函数
析构函数
析构函数
析构函数
析构函数
析构函数

4.2、赋值类的运算符

= += -= *= /= %= &= |= ^=
表达式:L # R
1> 左操作数只能是一个左值,
2> 右操作数既可以是左值,也可以是右值。 
	
a.operator+=(b);
a += b;
	
类名& operator#(const 类名& other){		
}
//参考案例: 11Complex.cpp 实现+=运算符重载
// '+='
Complex& operator+=(const Complex& other){
	m_r += other.m_r;
	m_i += other.m_i;
	return *this;
}

4.3、单目运算符的重载

-(负)
~(取反) 
!(非)
表达式:
	#O    // O 操作数
1> 操作数可以是左值,也可以是右值
	-a;
	-100;
2> 返回值是一个右值
	b = -a;
	-a = b;  // error
	
重载的函数: 
	#O   // O.operator#();
	const 类名 operator#(void)const{}
//参考案例:01operator.cpp 
#include <iostream>
using namespace std;
class Integer{
public:
	Integer(int a, int b):m_a(a),m_b(b){}
	// '-'
	const Integer operator-(void)const {
		return Integer(-m_a, -m_b);
	}
	void show(void){
		cout << m_a << ":" << m_b << endl;
	}
private:
	int m_a;
	int m_b;
};

int main(int argc, const char *argv[])
{
	Integer i1(1,2);
	Integer i2 = -i1;
	i2.show();
	return 0;
}
//结果:
-1:-2

4.4、自增和自减运算符的重载

1. 前加加/前减减运算符: 先运算后使用
	表达式:#O
	1> 操作数是一个左值,
	返回结果是一个左值,
	返回值就是返回自身。
		
	// #O   ---> O.operator#()
	类名& operator#(void){}
		
2. 后加加/后减减运算符: 先使用后运算
	表达式:O#
	1> 操作数是一个左值, 
		返回值是一个右值,
		返回的是操作数的备份。
	b = a++;
	// O#  ---> O.operator#(哑元);
	const 类名 operator#(哑元){}
#include <iostream>
using namespace std;
class Integer{
	int m_a;
	int m_b;
public:
	Integer(int a, int b):m_a(a),m_b(b){}
	// 前加加 
	Integer& operator++(void){
		cout << "前++" << endl;
		++m_a;
		++m_b;
		return *this;
	}
	
	// 后加加 
	const Integer operator++(int){
		cout << "后++" << endl;
		Integer i = *this;
		++m_a;
		++m_b;
		return i;
	}


	void show(){
		cout << m_a << ":" << m_b << endl;
	}

};

int main(int argc, const char *argv[])
{
	Integer i1(100,200);
	Integer i2 = ++i1; 
	i1.show();
	i2.show();

	Integer i3(1000,2000);
	Integer i4 = i3++;
	i3.show();
	i4.show();

	return 0;
}
//结果:
前++
101:201
101:201
后++
1001:2001
1000:2000

4.4、插入和提取运算符的重载 << >>

Complex i1;
cin >> i1;
cout << i1 << endl;
	
#include <iostream>
istream cin;  --> 输入类 
ostream cout; --> 输出类
	
表达式: cin >> R;  operator>>(cin, R);
friend istream& operator>>(istream& is, 类名& R);
	
	
表达式: cout << R; operator<<(cout, R);
friend ostream& operator<<(ostream& os, const 类名& R);
#include <iostream>
using namespace std;
class Complex{
public:
	Complex():m_r(0),m_i(0){}
	Complex(int r, int i):
		m_r(r),m_i(i){
		cout << "构造函数" << endl;	
	}
	~Complex(void){
		cout << "析构函数" << endl;
	}
	Complex(const Complex& other):
		m_r(other.m_r),m_i(other.m_i){
		cout << "拷贝构造函数" << endl;	
	}
	Complex& operator=(const Complex& other){
		cout << "拷贝赋值函数" << endl;
		if(&other != this){
			m_r = other.m_r;
			m_i = other.m_i;
		}
		return *this;
	}
	/*
	 * 第一个const : 返回值是右值
	 * 第二个const : 不可以修改右操作数
	 * 第三个const : 不可以修改左操作数
	 * */
	const Complex operator+(const Complex& other)const{
		cout << "+运算符重载" << endl;
		       // 临时对象
		return Complex(m_r+other.m_r,m_i+other.m_i);
	}
	const bool operator>(const Complex& other)const{
		return (m_r>other.m_r)?true:false; 
	}
	Complex& operator+=(const Complex& other){
		m_r += other.m_r;
		m_i += other.m_i;
		return *this;
	}
	// 声明 cin
	friend istream& operator>>(istream& , Complex& R);
    // 声明 cout
	friend ostream& operator<<(ostream& , const Complex& R);

	void show()
	{
		cout << m_r << "+" << m_i << "i" << endl;
	}
	private:
	int m_r;
	int m_i;
};
// 实现 cin
istream& operator>>(istream& , Complex& R)
{
	cin >> R.m_r >> R.m_i;
	return cin;
}
// 实现 cout
ostream& operator<<(ostream& , const Complex& R)
{
	cout << R.m_r << "+" << R.m_i;
	return cout;
}
int main(int argc, const char *argv[])
{
	Complex i1;
	cin >> i1;
	cout << i1 << endl;
	return 0;
}
//输入:
12 24
//输出:
12+24
析构函数

4.6、不可以实现重载的运算符

1> 作用域限定操作符 :: 
2> 数据类型长度计算操作符:sizeof
3> 条件运算符 ? :
4> 直接成员访问 .

4.7、其他操作符 [] ()

1> 数组类中的 [] 运算符重载
2> 函数对象的实现 ()运算符的重载

作业:

继续重构自己的myString类
= += < > >= <= != ==

posted on 2020-12-19 21:01  八杯水  阅读(201)  评论(0)    收藏  举报