C++中类的前身
1> 面向对象三大特征:封装、继承、多态
2> 封装:将能够实现某一事物的所有万事万物都封装到一起,包括成员属性(成员变量),行为(功能函数)都封装在一起,我们称之为类。并向外部提供公共的接口,用户可以通过该接口来控制该类实例化的对象。
3> 只需将C++中的结构体的struct改成class就是类的定义
4> 类的定义格式
class 类名 { 访问权限: 成员; };
类的说明
1> 类中的访问权限有三种:public、protected、private
public:该权限下的成员,可以在类内、子类中以及类外被访问
protected:该权限下的成员,可以在类内、子类中被访问,类外不能被访问
private:该权限下的成员只能被类内访问,类外、子类中都不能被访问
在类中,默认的访问权限是private
2> 类中可以有成员属性和成员函数,一般成员属性设置成私有、成员变量设置成公共的
3> 类中的成员函数可以访问类中所有权限下的成员,包括私有成员
4> 每种访问权限,可以在类中出现多次,每个访问权限从关键字开始,到下一个关键字为止或者整个类体结束。
5> 一般将同一权限下的所有成员放在一起
6> 如果非要在类外对私有成员或受保护的成员进行读取或者写入,需要在类内提供相关公共的接口来完成
7> 类的访问权限是针对于类体而言,而不是针对于类对象而言
cpp
#include <iostream>
using namespace std;
//定义一个 Person 类
class Person
{
public:
string name = "张三";
char sex; //性别
private:
int age = 18;
protected:
int money;
public:
//定义展示函数
void show()
{
cout<<"name = "<<name<<endl; //公共权限下的成员,类内可以被访问
cout<<"sex = "<<sex<<endl; //公共权限下的成员,类内可以被访问
cout<<"age = "<<age<<endl; //私有权限下的成员,类内可以被访问
cout<<"money = "<<money<<endl; //受保护权限下的成员,类内可以被访问
}
//定义设置年龄函数
void set_age(int a)
{
if(a<0)
{
cout<<"设置失败"<<endl;
return;
}
age = a;
}
//定义获取年龄的函数
int get_age()
{
return age;
}
};
int main()
{
//class Person p1; //使用类定义一个变量
Person p1; //使用类,定义一个变量 ---> 使用Person类实例化一个对象
p1.name = "二狗"; //类中公共权限下的成员,类外可以被访问
p1.sex = 'M'; //类中公共权限下的成员,类外可以被访问
//p1.age = 18; //类中私有权限下的成员,类外无法被访问
//p1.money = 1000000; //类中受保护的成员,类外无法被访问
p1.show(); //类中公共权限下的成员,类外可以被访问
//设置年龄
p1.set_age(118);
cout<<"p1.age = "<<p1.get_age()<<endl;
return 0;
}
练习:定义一个矩形(Rectangle)类,拥有私有成员属性:宽度(width)、高度(hight)
公共成员函数:
初始化函数:void init(int w, int h)
获取宽度函数:get_w();
设置宽度函数:set_w(int w);
获取高度函数:get_h();
设置高度函数:set_h(int h);
获取周长函数:get_perimeter();
获取面积函数:get_area();
在主程序中实例化对象,并测试下相关函数
cpp
#include <iostream>
using namespace std;
class Rectangle //定义一个矩形类
{
//私有成员属性
private:
int width; //矩形的宽度
int hight; //矩形的高度
public:
//初始化函数
void init(int w,int h)
{
if(w<=0 || h<=0)
{
cout<<"初始化失败"<<endl;
return;
}
width=w;
hight=h;
}
//设置宽度函数
void set_w(int w)
{
width=w;
}
//设置高度函数
void set_h(int h)
{
hight=h;
}
//获取宽度
int get_w()
{
return width;
}
//获取周长
int get_peri()
{
return 2*width+2*hight;
}
//获取面积
int get_area()
{
return width*hight;
}
};
int main()
{
Rectangle r1; //实例化一个矩形对象
r1.init(3,5); //初始化矩形
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
//重新设置矩形的宽度
r1.set_w(10);
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
return 0;
}
练习:在上面的案例上面加一个功能:比较两个矩形框是否相等 bool judge()
判断两个矩形相等的原则:width==width && heigh==heigh
全局函数版本: bool judge(Rectangle r1, Rectangle r2);
cpp
#include <iostream>
using namespace std;
class Rectangle //定义一个矩形类
{
//私有成员属性
private:
int width; //矩形的宽度
int hight; //矩形的高度
public:
//初始化函数
void init(int w,int h)
{
if(w<=0 || h<=0)
{
cout<<"初始化失败"<<endl;
return;
}
width=w;
hight=h;
}
//设置宽度函数
void set_w(int w)
{
width=w;
}
//设置高度函数
void set_h(int h)
{
hight=h;
}
//获取宽度
int get_w()
{
return width;
}
//获取周长
int get_peri()
{
return 2*width+2*hight;
}
//获取面积
int get_area()
{
return width*hight;
}
//获取矩形框高度
int get_h()
{
return hight;
}
//定义成员函数,比较两个矩形是否相等
bool judge(Rectangle &r1, Rectangle &r2)
{
//比较逻辑
if(r1.width==r2.width && r1.hight==r2.hight)
{
return true;
}
return false;
}
//定义成员函数,比较当前矩形框和其他传入的矩形框是否相等
bool judge(Rectangle &other)
{
//比较逻辑
if(width==other.width && hight==other.hight)
{
return true;
}
return false;
}
};
//定义全局函数完成求两个矩形框是否相等
bool judge(Rectangle &r1, Rectangle &r2)
{
//比较逻辑
if(r1.get_w()==r2.get_w() && r1.get_h()==r2.get_h())
{
return true;
}
return false;
}
int main()
{
Rectangle r1; //实例化一个矩形对象
r1.init(3,5); //初始化矩形
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
//重新设置矩形的宽度
r1.set_w(10);
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
//实例化另一个矩形框
Rectangle r2;
r2.init(10,10);
//判断两个矩形框是否相等
/*
if(judge(r1,r2)) //调用全局函数
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}
*/
/*调用成员函数判断两个矩形框是否相等
if(r1.judge(r1, r2))
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}*/
if(r1.judge(r2))
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}
return 0;
}
分文件编译
头文件
cpp
#ifndef RECANTLE_H
#define RECANTLE_H
#include <iostream>
using namespace std;
class Rectangle //定义一个矩形类
{
//私有成员属性
private:
int width; //矩形的宽度
int hight; //矩形的高度
public:
//初始化函数
void init(int w,int h);
//设置宽度函数
void set_w(int w);
//设置高度函数
void set_h(int h);
//获取宽度
int get_w();
//获取周长
int get_peri();
//获取面积
int get_area();
//获取矩形框高度
int get_h();
//定义成员函数,比较两个矩形是否相等
bool judge(Rectangle &r1, Rectangle &r2);
//定义成员函数,比较当前矩形框和其他传入的矩形框是否相等
bool judge(Rectangle &other);
};
//全局函数的声明
bool judge(Rectangle &r1, Rectangle &r2);
#endif // RECANTLE_H
源文件
cpp
#include "recantle.h"
//初始化函数
void Rectangle::init(int w,int h)
{
if(w<=0 || h<=0)
{
cout<<"初始化失败"<<endl;
return;
}
width=w;
hight=h;
}
//设置宽度函数
void Rectangle::set_w(int w)
{
width=w;
}
//设置高度函数
void Rectangle::set_h(int h)
{
hight=h;
}
//获取宽度
int Rectangle::get_w()
{
return width;
}
//获取周长
int Rectangle::get_peri()
{
return 2*width+2*hight;
}
//获取面积
int Rectangle::get_area()
{
return width*hight;
}
//获取矩形框高度
int Rectangle::get_h()
{
return hight;
}
//定义成员函数,比较两个矩形是否相等
bool Rectangle::judge(Rectangle &r1, Rectangle &r2)
{
//比较逻辑
if(r1.width==r2.width && r1.hight==r2.hight)
{
return true;
}
return false;
}
//定义成员函数,比较当前矩形框和其他传入的矩形框是否相等
bool Rectangle::judge(Rectangle &other)
{
//比较逻辑
if(width==other.width && hight==other.hight)
{
return true;
}
return false;
}
//定义全局函数完成求两个矩形框是否相等
bool judge(Rectangle &r1, Rectangle &r2)
{
//比较逻辑
if(r1.get_w()==r2.get_w() && r1.get_h()==r2.get_h())
{
return true;
}
return false;
}
测试文件
cpp
#include"recantle.h" //引入自定义的头文件
int main()
{
Rectangle r1; //实例化一个矩形对象
r1.init(3,5); //初始化矩形
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
//重新设置矩形的宽度
r1.set_w(10);
cout<<"矩形的周长为:"<<r1.get_peri()<<endl;
cout<<"矩形的面积为:"<<r1.get_area()<<endl;
//实例化另一个矩形框
Rectangle r2;
r2.init(10,10);
//判断两个矩形框是否相等
/*
if(judge(r1,r2)) //调用全局函数
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}
*/
/*调用成员函数判断两个矩形框是否相等
if(r1.judge(r1, r2))
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}*/
if(r1.judge(r2))
{
cout<<"两个矩形框相等"<<endl;
}else
{
cout<<"两个矩形框不相等"<<endl;
}
return 0;
}
this指针
1> this指针是类中系统为所有非静态成员函数提供的一个隐藏的形参指针,指代当前对象的起始地址
2> 指代当前对象,哪个对象使用我,我就指向哪个对象
3> 在非静态成员函数中,如果调用了类中的成员(成员函数、成员变量),即使没有加this,系统也默认加了this。
局部名称屏蔽成员名称除外
4> this的原型: 类名 * const this;
5> 必须使用this的情况:
1、当形参名和成员名同名时,可以使用this用于区分
2、在拷贝赋值函数中,用于返回自身引用时,必须使用this(后期讲)
cpp
#include <iostream>
using namespace std;
class Stu
{
string name = "zhangsan";
int age = 18;
public:
//非静态成员函数
void show()
{
cout<<"show::this = "<<this<<endl;
//cout<<"name = "<<name<<" age = "<<age<<endl;
cout<<"name = "<<this->name<<" age = "<<this->age<<endl;
}
//定义设置函数
void set_data(string name, int a) //this形参的原型: Stu * const this;
{
cout<<"set_data::this = "<<this<<endl;
this->name = name; //将形参name,赋值给成员变量name
this->age = a; //可以通过this改变指针指向的内存空间中的值
//this = NULL; //不能通过this改变指针的值
this->show(); //成员函数中调用另一个成员函数
}
};
类的大小
1> 一个空类的大小为 1 字节,用于占位作用,后期如果有成员变量,就会将该1字节空间分配给成员使用
2> 类中的成员函数不占类体的大小,成员函数只有被调用时,才会分配空间
3> 只有类的成员属性才占内存空间,其类的大小要遵循字节对齐原则
4> 如果类中有虚函数,则会多一个虚指针的空间 (后期讲)
cpp
#include <iostream>
using namespace std;
//一个空类的大小为 1字节
class A
{};
//成员 变量占内存空间,要符合字节对齐原则
class B
{
int value_a;
char value_b;
short value_c;
char value_d;
double value_e;
short value_f; //11112033400000005555555566000000
};
//定义一个类,既有成员属性也有成员变量,类的成员函数是不占类体的空间的
class C
{
int a;
void show()
{
int b;
cout<<"b = "<<b<<" a = "<<a<<endl;
}
void display()
{
this->show();
}
};
//当类中出现虚函数时,系统会给类默认提供一个虚指针,无论有多少虚函数,都只提供一个虚函数指针
class D
{
int value_a; //成员变量
//非虚函数
void show()
{}
//定义虚函数
virtual void diaplay()
{}
//虚函数
virtual void fun()
{}
};
int main()
{
cout << sizeof(A) << endl; // 1
cout << sizeof(B) << endl; // 32
cout << sizeof(C) << endl; // 4
cout << sizeof(D) << endl; // 16
return 0;
}
类中特殊成员函数
1> 当定义一个空类时,即使没有手动定义某些函数,C++系统会默认自动为其提供一些特殊成员函数
2> 这些函数也可以程序员手动定义,当手动定义后,系统就不再提供默认的了
3> 这些函数也无需程序员手动调用,在特殊时期,C++系统会自动调用
4> 种类:构造函数、析构函数、拷贝构造函数、拷贝赋值函数、移动构造函数、移动赋值函数、取地址运算符重载函数
构造函数
1> 构造函数是在使用类实例化对象时,用于给类对象分配内存空间以及对成员进行初始化工作时使用
2> 定义格式
1、构造函数没有返回值,不需要写返回值类型 2、函数名与类同名 3、可以有参,也可以无参 4、一般为公共权限 5、格式: 类名(形参列表){函数体内容}
3> 调用时机:无需程序员手动调用,当使用一个类实例化对象时,系统自动调用构造函数
4> 调用格式:
无参构造: 类名 对象名;
例如:string s1;
有参构造: 类名 对象名(实参列表);
例如:string s2("hello a");
string s3(5, 'A');
5> 一个类中可以定义多个构造函数,要求参数必须不同,这些构造函数构成重载关系
虽然一个类中可以定义多个构造函数,但是一个类对象的只能使用一个构造函数构造出来
6> 一个类中,如果没有显性定义任何构造函数,那么系统会默认提供一个无参构造函数
但凡类中提供任意一个构造函数,系统就不再提供默认的无参构造函数了,如果非要使用无参构造函数,需要程序员手动定义无参构造函数
7> 构造函数的形参列表也可以设置默认参数,但是要注意是否跟其他重载的函数冲突
8> 构造函数的初始化工作是在初始化列表中完成的。
初始化列表:在构造函数函数头部后面,由冒号引出,完成初始化工作
格式: 类名(形参类型1 形参1, 形参类型2 形参2,。。。, 形参类型n 形参n) : 成员变量1(形参1),成员变量2(形参2),。。。,成员变量n(形参n) {}
cpp
#include <iostream>
using namespace std;
//定义一个类
class Stu
{
private:
const string name; //如果类中有const修饰的成员变量,必须使用初始化列表完成对其进行初始化工作
int age;
public:
//无参构造函数
Stu():name("二狗")
{
//this->name = "二狗";
this->age = 100;
cout<<"Stu::无参构造"<<endl;
}
//定义有参构造函数
Stu(string n) :name(n)
{
//this->name = n;
cout<<"Stu::有参构造1"<<endl;
}
//定义有参构造
//Stu(string n="大毛", int a=20) //带默认参数的构造函数
Stu(string name, int age) :name(name), age(age) //使用初始化列表完成对成员的初始化工作
{
//此处是给成员变量赋值,而不是初始化
//this->name = n;
//this->age = a;
cout<<"Stu::有参构造2"<<endl;
}
//定义展示函数
void show()
{
cout<<"name = "<<name<<" gae = "<<age<<endl;
}
};
int main()
{
Stu s1; //系统会自动调用该类中的无参构造函数
s1.show();
cout<<"***************************\n";
Stu s2("张三"); //调用自定义的有参构造
s2.show();
cout<<"***************************\n";
Stu s3("张三", 18); //调用自定义的有参构造
s3.show();
return 0;
}
9> 必须使用构造函数的初始化列表的情况
1、如果类的成员变量中有常成员变量,对该成员的初始化工作,必须使用初始化列表完成
2、如果类中有引用成员,那么对该成员也必须使用初始化列表完成
3、如果构造函数的形参名和成员变量同名时,可以使用初始化列表来解决
4、如果一个类中,有另一个类的对象,对该成员对象的初始化工作需要在初始化列表中显性定义该成员对象的有参构造,否则系统会自动调用其无参构造
cpp
#include <iostream>
using namespace std;
//定义一个汽车类
class Car
{
private:
string name;
string color;
public:
Car() {cout<<"Car::无参构造"<<endl;}
Car(string n, string c):name(n), color(c)
{cout<<"Car::有参构造"<<endl;}
};
//定义一个类
class Stu
{
private:
const string name; //如果类中有const修饰的成员变量,必须使用初始化列表完成对其进行初始化工作
int age;
int &score; //引用成员 必须在构造函数的初始化列表中进行初始化工作
Car c;
public:
//无参构造函数
Stu():name("二狗"), score(*new int(520)), c("擎天柱","red")
{
//this->name = "二狗";
this->age = 100;
cout<<"Stu::无参构造"<<endl;
}
//定义有参构造函数
Stu(string n, int &score) :name(n), score(score)
{
//this->name = n;
cout<<"Stu::有参构造1"<<endl;
}
//定义有参构造
//Stu(string n="大毛", int a=20) //带默认参数的构造函数
Stu(string name, int age, int &score) :name(name), age(age), score(score) //使用初始化列表完成对成员的初始化工作
{
//此处是给成员变量赋值,而不是初始化
//this->name = n;
//this->age = a;
cout<<"Stu::有参构造2"<<endl;
}
//定义展示函数
void show()
{
cout<<"name = "<<name<<" gae = "<<age<<endl;
}
};
int main()
{
Stu s1; //系统会自动调用该类中的无参构造函数
s1.show();
cout<<"***************************\n";
int s = 99;
Stu s2("张三", s); //调用自定义的有参构造
s2.show();
cout<<"***************************\n";
Stu s3("张三", 18, s); //调用自定义的有参构造
s3.show();
return 0;
}
使用C++手动封装一个顺序表,包含成员指针变量,成员变量N个
cpp
#include <iostream>
using namespace std;
//类型重命名
using datatype = int; //typedef int datatype;
//定义一个顺序表类
class SeqList
{
private:
datatype *data; //指向堆区空间的指针
int size = 0; //数组的大小
int len = 0; //顺序表实际长度
public:
//无参构造
/*SeqList():data(new datatype[10]), size(10), len(0)
{
cout<<"无参构造"<<endl;
}*/
//有参构造
SeqList(int s):data(new datatype[s]), size(s), len(0)
{
cout<<"有参构造"<<endl;
}
//要实现的函数
//判空函数
bool empty()
{
return len==0;
}
//判满函数
bool full()
{
return len==size;
}
//添加数据函数
bool add(datatype e)
{
if(full())
{
return false;
}
len++;
data[len] = e;
return true;
}
//求当前顺序表的实际长度
int length()
{
return len;
}
//任意位置插入函数
bool insert_pos(int pos, datatype e)
{
if(full()||pos<0||pos>len)
{
return false;
}
for(int i=len;i>pos;i--)
{
data[i]=data[i-1];
}
data[pos]=e;
len++;
return true;
}
//任意位置函数函数
bool delete_pos(int pos)
{
if(empty()||pos<0||pos>=len)
{
return false;
}
for(int i=pos;i<len-1;i++)
{
data[i]=data[i+1];
}
len--;
return true;
}
//访问容器中任意一个元素 at
datatype &at(int index)
{
if(index<0||index>=len)
{
return data[-1];
}
return data[index];
}
//君子函数:二倍扩容
void expend()
{
size *=2;
datatype *newdata =new datatype[size];
for(int i=0;i<len;i++)
{
newdata[i] = data[i];
}delete []data;
data = newdata;
}
//释放顺序表
void seqfree()
{
delete[]data;
data =NULL;
size = 0;
len = 0;
}
};
int main()
{
SeqList S(5);
S.add(1);
S.add(2);
S.insert_pos(1,3);
cout <<"长度 = "<<S.length()<<endl;
return 0;
}