class
认识class
//一个简单的类的创建
#include<iostream>
using namespace std;
class stu{
public:
string name;
int year;
void set_value(string name,int year);
void show(){
cout << '姓名:'<< name << '年龄:'<< year << endl;
}
};
void stu::set_value(string name,int year){
name = name1;
year = year1;
}
int main(){
stu student;
student.set_value('鹤清',20);
student.show();
}
类的访问权限
- 在类的内部,无论成员被申明为public还是private,都可以访问
- 在类的外部,只能访问public成员,不能访问private和protected成员
- 成员函数算类的内部的部分
- 如果不设置权限,则默认全部内容都为private
简单使用类
-
类的成员函数可以直接访问该类其他成员函数(可递归)
#include<iostream> using namespace std; class stu{ public: string name; int year; int times = 0; void set_value(string name,int year); void show(){ if(times++ > 10) return; //终止递归条件:运行10次 cout << '姓名:'<< name << '年龄:'<< year << endl; } }; int main(){ stu student; student.set_value('鹤清',20); student.show(); }
-
类的成员函数可以重载,可以使用默认参数
-
类的指针的用法与结构体指针用法相同
-
类的成员可以是任意数据类型(类中枚举)
class stu{ public: string name; int year; enum{girl=1,boy=2}; void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; if(sex==girl){;} } };
-
类可以创建对象数组,就像结构体数组一样
-
对象可以作为实参传递给函数,一般传引用
-
在类的外部,一般不直接访问(读和写)对象的成员,而是用成员函数
class stu{ public: string name; int year; int get_age(){ return age; } void set_age(int age1){ age = age1; } void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; int main(){ stu student; student.set_value('鹤清',20); //student.age = 21 #一般不这么用
-
对象一般不用memset()清空成员变量,可以写一个专用于清空成员变量的成员函数
class stu{ public: string name; int year; int times = 0; void initdata(){ //清空全部的成员变量 name.clear(); age = 0; } void set_value(string name,int year); void show(){ if(times++ > 10) return; //终止递归条件:运行10次 cout << '姓名:'<< name << '年龄:'<< year << endl; } };
-
对类和对象用sizeof运算意义不大,一般不用
-
用结构体描述纯粹的数据,用类描述对象
-
在类的声明中定义的函数都将自动成为内联函数;在类的声明之外定义的函数如果使用了inline限定符,也是内联函数
class stu{ public: string name; int year; void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; inline void stu::set_value(string name,int year){ name = name1; year = year1; }
-
为了区分类的成员变量和成员函数的形参,把成员变量名加m_前缀或_后缀,如m_name或name_
-
类的分文件编写。一般情况下,把声明类的代码放在头文件中,把成员函数定义的代码放在源文件中
构造函数和析构函数(自动执行)
- 构造函数:在创建对象时,自动的进行初始化工作
- 析构函数:在销毁对象前,自动的完成清理工作
构造函数
-
访问权限必须是public
-
函数名必须与类名相同
-
没有返回值,也不写void
-
可以有参数,可以重载,可以有默认参数
-
创建对象时会自动调用一次,不能手工调用
class stu{ public: string m_name; int m_year; char m_memo[301]; //备注 stu(){ //构造函数 m_name.clear(); m_age = 0; memset(m_memo,0,sizeof(m_memo)); } void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; int main(){ stu student; //student.set_value('鹤清',20); student.show(); }
-
创建对象的时候不要在对象名后面加空的圆括号,编译器误认为是声明函数
-
在构造函数名后面加括号和参数不是调用构造函数,是创建匿名对象
class stu{ public: string m_name; int m_year; char m_memo[301]; //备注 stu(){ //构造函数 m_name.clear(); m_age = 0; memset(m_memo,0,sizeof(m_memo)); } stu(string name){ //构造函数 //m_name.clear(); //m_age = 0; //memset(m_memo,0,sizeof(m_memo)); stu(); //代替上面的三行代码,其实会遗留bug,真正意义不是调用构造函数,而是创建一个匿名对象,也叫临时对象。如果类里面有指针,就会很危险 m_name = name; } void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } };
析构函数
-
访问权限必须是public
-
函数名必须在类名前加~
-
没有返回值,也不写void
-
没有参数,不能重载
-
销毁对象前只会自动调用一次,但是可以手工调用(手工调用应用场景很少)
class stu{ public: string m_name; int m_year; char m_memo[301]; //备注 stu(){ //构造函数 m_name.clear(); m_age = 0; memset(m_memo,0,sizeof(m_memo)); } ~stu(){ cout << "调用了析构函数\n"; } void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; int main(){ stu student; //student.set_value('鹤清',20); student.show(); }
拷贝构造函数
class stu{
public:
string m_name;
int m_year;
char m_memo[301]; //备注
stu(){ //构造函数
m_name.clear();
m_age = 0;
memset(m_memo,0,sizeof(m_memo));
}
void set_value(string name,int year);
void show(){
cout << '姓名:'<< name << '年龄:'<< year << endl;
}
};
int main(){
stu student1;
student1.m_name='鹤清';student1.m_year=21;
//两种方式
stu student2(student1);
stu student3=student1;
}
-
语法: 类名(const 类名&对象名)
-
访问权限必须是public
-
函数名必须与类名相同
-
没有返回值,不写void
-
如果类中定义了拷贝构造函数,编译器将不提供拷贝构造函数
class stu{ public: string m_name; int m_year; char m_memo[301]; //备注 stu(){ //构造函数 m_name.clear(); m_age = 0; memset(m_memo,0,sizeof(m_memo)); } stu(const stu&gg){//拷贝构造函数 m_name = "漂亮的"+gg.m_name; m_age = gg.m_age - 1; } void set_value(string name,int year); void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; int main(){ stu student1; student1.m_name='鹤清';student1.m_year=21; //两种方式 stu student2(student1); stu student3=student1; }
初始化列表
class stu{
public:
string m_name;
int m_year;
char m_memo[301]; //备注
stu():m_name("鹤清"),m_year(21)
{ //构造函数
;
}
void show(){
cout << "姓名:"<< m_name << "年龄:"<< m_year << endl;
}
};
int main(){
stu student1;
student1.show();
}
- 如果成员已经在初始化列表中,则不应该在构造函数中再次赋值
- 初始化列表的括号中可以是具体的值,也可以是构造函数的形参名,还可以是表达式
- 初始化列表与赋值有本质的区别,如果成员是类,使用初始化列表调用的是拷贝构造函数,而赋值则是先创建对象(调用普通构造函数),然后再赋值
- 如果成员是类,初始化列表对性能略有提升
- 如果成员是常量和引用,必须使用初始列表,因为常量和引用只能在定义的时候初始化
- 如果成员是没有默认构造函数的类,则必须使用初始列表
const修饰成员函数
在类的成员函数后面加const关键字,表示在成员函数中保证不会修改调用对象的成员变量
实际开发中,如果成员函数不会修改成员变量,就应该加const修饰(编程规范)
- mutable可以突破const的限制,被mutable修饰的成员变量,将永远处于可变的状态
- 非const成员函数可以调用const成员函数和非const成员函数
- const成员函数不能调用非const成员函数
- 非const对象可以调用const修饰的成员函数和非const修饰的成员函数
- const对象只能调用const修饰的成员函数,不能调用非const修饰的成员函数
this 指针
class stu{
public:
string m_name;
int m_year;
char m_memo[301]; //备注
stu(){ //构造函数
m_name.clear();
m_age = 0;
memset(m_memo,0,sizeof(m_memo));
}
void set_value(string name,int year);
const stu& pk(const stu& g){
if (g.m_year<m_year) return g;
return *this;
}
void show(){
cout << '姓名:'<< name << '年龄:'<< year << endl;
}
};
int main(){
stu s1("张三",1),s2("李四",2),s3("王五",3);
const stu& s4 = s1.pk(s2).pk(s3);
s4.show();
}
静态成员
类的静态成员包括静态成员变量和静态成员函数
用静态成员可以变量实现多个对象之间的数据共享,比全局变量更有安全性
用static关键字把类的成员变量声明为静态,表示它在程序中是共享的
静态成员变量不会在创建对象的时候初始化,必须在程序的全局区用代码清晰的初始化(用范围解析运算符::)
静态成员使用类名加范围解析运算符::就可以访问,不需要创建类对象
class stu{
public:
string m_name;
static int m_year; //静态变量
stu(const string& name,int year){
m_name = name;
m_year = year;
}
void show(){
cout << '姓名:'<< name << '年龄:'<< year << endl;
}
};
int stu::m_year = 8; //不能放在类中和main函数中,不创建对象也可以访问
int main(){
stu student;
student.set_value('鹤清',20);
student.show();
}
- 如果类的成员声明为静态的,就可以把它与类的对象独立开来
- 静态成员函数只能访问静态成员,不能访问非静态成员
- 静态成员函数中没有this指针
- 非静态成员函数可以访问静态成员
- 私有静态成员变量在类外无法访问
- const静态成员变量可以在定义类的时候初始化
继承
#include <iostream>
using namespace std;
// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
友元
如果要访问类的私有成员变量,调用类的公有成员函数是唯一的办法,而类的私有成员函数则无法访问。友元提供了另一访问类的私有成员的方案,友元有三种:
-
友元全局函数:在友元全局函数中,可以访问另一个类的所有成员
class stu{ friend void show_address(); //声明为友元 private: string m_address; public: string m_name; static int m_year; //静态变量 stu(const string& name,int year){ m_name = name; m_year = year; } void show(){ cout << '姓名:'<< name << '年龄:'<< year << endl; } }; void show_address{ stu ss; cout << ss.m_address<< endl; } int main(){ stu student; student.set_value('鹤清',20); student.show(); }
-
友元类:在友元类所有成员函数中,都可以访问另一个类的所有成员
-
友元关系不能被继承
-
友元关系是单向的,不具备交换性
若类B是类A的友元,类A不一定是类B的友元。类B是类A的友元,类C是类B的友元,类C不一定是类A的友元
-
-
友元成员函数:在友元类某成员函数中,可以访问另一个类的所有成员