C++ 学习笔记06
一、析构函数
创建对象--->构造函数--->析构函数--->删除对象
1.作用
析构函数是一个特殊的成员函数 作用与构造函数相反
功能:当对象脱离其作用域(对象所在的函数已经调用完毕) 系统自动调用析构函数 析构函数往往做"清理善后"工作
声明:没有返回值 名字必须与类名同名 没有参数 名字前 ~
析构函数不能有参数,不能被重载
示例1
using namespace std;
class Person
{
private:
int age;
public:
Person(int age):age(age)
{
cout<<"构造"<<endl;
}
int getAge()
{
return age;
}
~Person()
{
cout<<"析构"<<endl;
}
};
int main()
{
cout<<”1---------------------”<<endl;
Person* p = new Person(19);//构造
cout<<”2---------------------”<<endl;
delete p;//析构
cout<<”3---------------------”<<endl;
Person p1(20);//构造
cout<<”4---------------------”<<endl;
{//花括号本身就是一个作用域
Person p2(30);//构造 析构
}
cout<<”5---------------------”<<endl;
}
//main函数结束后 输出p1的析构
2.默认析构函数
如果没有显式的定义析构函数,会生成默认析构函数。
默认的析构函数:没有参数,没有逻辑
~Person()
{
}
练习1
设计一个Book类 包含图书的书名,作者成员 其中:书名和作者用字符型指针
char* name;
动态创建数组来保存字符串
char* author;
1.构造函数为作者及书名指针开辟空间并将传入的 书名和作者拷贝到数组中
Book(const char* name,const char* author)
2.一个析构函数,删除书名和作者数组delete[] name
3.成员函数print()输出数据
#include <iostream>
#include <string.h>
using namespace std;
class Book
{
private:
char* name;
char* author;
public:
Book(const char* name, const char* author):
name(new char[20]),//使成员指针指向动态创建的数组
author(new char[20])
{
strcpy(this->name, name);//将参数name指向的字符串,拷贝到this->name指向的数组中
strcpy(this->author, author);
}
~Book()
{
delete[] name;//释放在构造函数中动态创建的数组
delete[] author;
}
void print()
{
cout<<name<<" "<<author<<endl;
}
};
int main()
{
Book b("c++ primer", "大神s");
b.print();
return 0;
}
二、常对象
常对象不能调用普通的成员函数。
1.常函数
1)常函数中的this是被const修饰的,在常函数中成员变量不能被修改,
2)常对象只能调用常函数
示例2
#include <iostream>
using namespace std;
class Person
{
private:
int age;
public:
Person(int age):age(age){}
void add() const //常函数的修饰方式,把const写在()的右边
{
//cout<<++age<<endl;//语法错误,常函数中不能修改成员变量
cout<<age<<endl;
cout<<”cont add”<<endl;
}
void add()//常函数算重载,普通对象优先调用普通函数
{
cout<<age<<endl;
cout<<”normal add”<<endl;
}
};
int main()
{
const Person p(20);
p.add();
Person p2(10);
p2.add();//普通对象也能调用常函数
}
2.声明常对象
const 类名 对象名(实参)
类名 const 对象名(实参)
注意常对象:
(1)成员变量必须有初值
(2)只能调用常成员函数
功能:希望对象所有成员的值不被修改
示例3
#include<iostream>
using namespace std;
class Person
{
private:
string name;
int age;
public:
Person(const string n,const int age);
void show();//常函数算重载
void show()const;
};
Person::Person(const string name,const int age):name(name),age(age)
{
}
void Person::show()
{
cout<<"我是"<<name<<" 今年"<<age<<endl;
}
void Person::show()const
{
cout<<"我是const "<<name<<" 今年"<<age<<endl;
}
int main()
{
const Person a("昆山龙哥",36);
a.show();//常对象只能调用常函数
Person b("昆山电池动车哥",43);
b.show();//普通对象优先调用普通函数
}
3.mutable
mutable修饰的成员变量可以在常函数中修改
示例4
#include <iostream>
using namespace std;
class Person
{
private:
string name;
int age;
mutable int a;//a可以在常函数中被修改
public:
Person(int age,string name,int a):age(age),name(name),a(a){}
void show()
{
cout<<age<<" "<<name<<endl;
}
void show() const
{
++a;//因为a是mutable修饰的成员,所以可以在常函数中修改
cout<<age<<"$$$"<<name<<" "<<a<<endl;
}
};
int main()
{
Person p(20, "小明",10);
p.show();
const Person p2(30, "小强",10);
p2.show();
}
三、友元
1.友元的概念
友元可以访问与其好友关系的类中的所有成员(包括private protected public)
使用friend关键字进行修饰
友元:友元函数和友元类
友元的特点:
- 友元是单向的
class B;
class A
{
friend class B;
};
//B是A的友元,但是A不是B的友元。
- 友元不能传递
B是A的友元,C是B的友元,A和C没有友元关系。 - 友元不能继承
2.友元函数
将普通函数声明为友元函数 friend + 函数声明
示例5
class Person;
void display(Person& p);
class Person
{
friend void display(Person& p);//声明display函数是Person类的友元函数,在display函数中可以访问Person类中的私有成员
private:
string name;
int age;
public:
Person(string name,int age):name(name),age(age)
{
}
void show()
{
cout<<name<<" "<<age<<endl;
}
};
void display(Person& p)//Person& p = a;
{
cout<<p.name<<" "<<p.age<<endl;//因为display是Person类中友元。所以可以直接访问Person类中私有成员
}
int main()
{
Person a("tom",10);
disPlay(a);
}
练习2
- 有个学生类Student
包括:私有成员:姓名 成绩
void setData(string name, int score)
给成员变量赋值,如果使用匿名对象对数组初始化,setData函数就没有用了。void show();
- 在main定义 student数组5个元素并赋值 Students[5]
- 设计一个全局友元函数 比较两个学生的成绩的高低
int compare(Student& a, Student& b);
a>b return 1; a<b return -1; 相等 0
- 求出最高分和最低分的学生
#include <iostream>
#include <string>
using namespace std;
class Student
{
friend int compare(Student& s1, Student& s2);// 友元的声明写在类的任何位置都可以,习惯于写在很醒目的位置
private:
string name;
int score;
public:
Student(string name, int score):
name(name), score(score)
{
}
void show()
{
cout<<name<<" "<<score<<endl;
}
};
int compare(Student& s1, Student& s2)
{
if(s1.score < s2.score)
{
return -1;
}
else if(s1.score > s2.score)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
Student students[5] = {
Student("小明", 18),
Student("小强", 81),
Student("小红", 60),
Student("小兰", 59),
Student("小绿", 61)
};
int maxIndex = 0;
int minIndex = 0;
for(int i = 1;i < 5;i++)
{
if(compare(students[maxIndex], students[i]) == -1)
{
maxIndex = i;
}
else if(compare(students[minIndex], students[i]) == 1)
{
minIndex = i;
}
}
students[maxIndex].show();
students[minIndex].show();
return 0;
}
3.友元类
friend + 类声明
示例6
#include <iostream>
#include <string>
using namespace std;
class Teacher;
class Student
{
friend class Teacher;//声明Teacher类是Student类的友元类,Teacher类中的每个成员函数都可以访问Student类中的私有成员。
private:
string name;
int score;
public:
Student(string name, int score):
name(name),
score(score)
{}
};
class Teacher
{
public:
void look(Student& s)
{
cout<<s.name<<" "<<s.score<<endl;//因为Student声明了Teacher类是Student类的友元类,所有Teacher类中的所有成员函数都能直接访问Student类中私有成员
}
};
int main()
{
Teacher t;
Student s("小张",19);
t.look(s);
}
四、C++中的异常机制
1.异常是什么?
异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。
2.检测异常的三个步骤:
检查(try) 抛出(throw) 捕捉(catch)
try
: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。throw
: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。catch
: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
3.try...catch语法
try
{
被检查语句
}
catch(异常处理的类型 变量名)
{
进行异常处理的语句;
}
示例7
#include<iostream>
using namespace std;
int mydiv(int a,int b)
{
if(b==0)//分母为0,说明发生了异常
{
cout<<"throw....."<<endl;
throw b;//抛出一个异常对象,异常对象可以是基本类型,也可以是类类型。
}
return a/b;
}
int main()
{
int a=10;
int b=0;
try
{
int result = mydiv(10,0);//因为mydiv函数可能会抛出异常,所以写在try语句块中用来捕捉异常。
如果mydiv抛出异常,会马上终止try语句块
如果mydiv没有抛出异常,会把try语句块执行完
cout<<result<<endl;
}
catch(int e)//在catch中处理try中抛出的异常,catch()中的对象,就是try中抛出的异常对象
{
cout<<"catch int "<<e<<endl;
}
}
示例8
捕捉多异常
#include <iostream>
using namespace std;
class ExceptA//专门用来表示异常的类
{
public:
void show()
{
cout<<"异常A"<<endl;
}
};
class ExceptB//专门用来表示异常的类
{
public:
void show()
{
cout<<"异常B"<<endl;
}
};
int div(int a, int b)
{
if(b == 0)
{
ExceptA e;
throw e;//抛出异常对象ExceptA 类型
}
return a/b;
}
double div(double a, double b)
{
if(b == 0)
{
ExceptB e;
throw e;//抛出异常对象ExceptB 类型
}
return a/b;
}
int main()
{
try
{
//同一时刻只能抛出一个异常
div(1, 0);//当这里抛出ExceptA 类型异常时,整个try就结束了
div(1.0, 0.0);
}
catch(ExceptA e)//专门用来处理ExceptA 类型异常的catch
{
e.show();
}
catch(ExceptB e)//专门用来处理ExceptB 类型异常的catch
{
e.show();
}
catch(...)//处理其他类型的异常
{
cout<<"other e"<<endl;
}
}
4.应用中的异常检测
int main()
{
do
{
if(1)
{
cout<<"异常1"<<endl;
break;//发现异常直接停止do-while结构
}
if(2)
{
cout<<"异常2"<<endl;
break;
}
}while(false);//do-while是为了break提供执行环境
return 0;
}
练习3
给出三角形三边 a、b、c 求三角形周长
只有a+b>c b+c>a a+c>b 才能构成三角形 设置异常处理 对不符合三角形条件的 输出警告信息 不予计算
class TriangleExcept
{
private:
int a;
int b;
int c;
public:
TriangleExcept(int a, int b, int c):a(a),b(b),c(c)
{}
void print()
{
cout<<a<<" "<<b<<" "<<c<<"can not a triangle"<<endl;
}
};
int triangleLength(int a, int b, int c)
{
if(a+b<=c || a+c<=b || b+c<=a)
{
TriangleExcept e(a, b, c);
throw e;//创建三角形异常对象,并且抛出异常
}
return a+b+c;
}
int main()
{
try
{
int len = triangleLength(2, 2, 3);
cout<<len<<endl;
}
catch(TriangleExcept e)
{
e.print();
}
return 0;
}
五、c++中静态成员
1.静态成员变量
- 属于整个类 静态成员变量只存储一份供所有对象使用
- 必须在类外单独初始化 而且只能全局进行 否则不会分配空间 编译报错
示例9
#include<iostream>
using namespace std;
class Stu
{
public:
static int num ;//静态成员变量,它不属于任何一个对象。
};
int Stu::num = 10;//初始化类中的静态成员变量,要写在cpp文件中
int main()
{
cout<<Stu::num<<endl;//访问静态成员变量
}
示例9 补充
class Student
{
private:
static int num;
public:
void addNum()
{
num++;
}
void showNum()
{
cout<<num<<endl;
}
};
int Student::num = 0;
int main()
{
Student s1;
s1.showNum();
s1.addNum();
s1.addNum();
Student s2;
s2.showNum();//因为所有的对象共用同一个静态成员变量,所以输出2
return 0;
}
2.静态成员函数
- 没有this指针,所以静态成员函数不能访问本类中的非静态成员,静态函数只能调用静态成员
- 静态成员函数和本类的任何对象都没有关系!!!
示例10
#include<iostream>
using namespace std;
class Stu
{
private:
int age;
public:
static int num;//静态成员变量
static int get_num()//静态成员函数
{
age = 10;//语法错误,因为静态成员函数中不能访问非静态成员
return num;//静态成员函数中只能使用静态成员
}
};
int Stu::num=89;
int main()
{
cout<<Stu::get_num()<<endl; //通过类名直接访问静态成员函数。
Stu s;
cout<<s.get_num()<<endl;//这么写也不算语法错误,但是没有逻辑意义,因为静态成员函数get_num()中没有指向对象s的指针。
}
示例10 补充
class Student
{
private:
static int num;
public:
Student()
{
num++;
}
~Student()
{
num--;
}
void addNum()
{
num++;
}
static void showNum()
{
cout<<num<<endl;
}
};
int Student::num = 0;
int main()
{
Student* p1 = new Student();
Student* p2 = new Student();
Student* p3 = new Student();
Student* p4 = new Student();
Student::showNum();//4
//p4->showNum();//这么写虽然语法允许,但是不符合逻辑
delete p1;
delete p2;
Student::showNum();//2
delete p3;
delete p4;
Student::showNum();//0
return 0;
}
六、设计模式——单例模式
设计模式就是编程的套路,解决指定问题的套路。它不是算法,是项目架构的设计套路。
设计模式是思想,没有固定的代码。