目录
一、常函数
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类
= += < > >= <= != ==
浙公网安备 33010602011771号