- 类
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
【1】定义
class 类名
{
访问权限:
成员属性;
访问权限:
成员方法;
}
访问权限:
public:共有的,类内、类外和子类中都可以访问
private:私有的,类内可以访问,类外和子类中都不能访问,类中默认是私有权限
protected:受保护的,类内和子类中可以访问,类外不可以访问(继承再说)
访问权限,是针对于一个类来说的
【2】类和结构体的区别
- 类的封装性比结构体更好,类中默认是私有权限,结构体中默认是共有权限
- 结构体默认是公有继承,类默认是私有继承
- C++中类就是由结构体演变来的
- 结构体一般用于实现某种数据结构时,类一般用于描述一类对象的性质
【3】this指针**
每一个类中的非静态成员函数,都有一个this指针,指向调用者,(非静态成员数隐藏 的形参)
谁调用this就指向谁,哪一个类对象调用成员函数,就会用该类对象的首地址,初始化形参this
原型:类类型 *const this; ----->指针的指向不能修改
需要使用this指针的场合
- 当形参和成员属性同名
- 拷贝赋值函数,需要返回自身的引用
#include <iostream>
using namespace std;
class Rec
{
private:
int len;
int wid;
public:
void set(int l,int w); //设置长和宽
void show(); //输出面积和周长
int get_l(); //获取长和宽
int get_wid();
};
void Rec::set(int len, int wid) //r1.set()
{
this->len = len;
this->wid = wid;
}
void Rec::show()
{
cout << "周长:" << (len+wid)*2 << endl;
cout << "面积:" << len*wid << endl;
}
int Rec::get_l()
{
return len;
}
int Rec::get_wid()
{
return wid;
}
int main()
{
Rec r1; //r1是一个类对象,实例化了一个类对象r1
r1.set(9,2);
Rec r2;
r2.set(9,2);
cout << "&r1=" << &r1 << endl;
return 0;
}
【4】类中的特殊成员函数
特殊的构造函数都是public权限
类中,会默认提供一些特殊的成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数
【5】构造函数
构造函数支持函数重载
构造函数,会在实例化类对象时,系统默认调用无参构造;
如果用户手动定义出了构造函数,系统将不再提供构造函数
类名()
{
//函数体
}
调用时机
栈区:实例化类对象时,自动调用
堆区:什么时候使用new申请空间,什么时候调用构造函数
当提供构造函数后
ii)构造函数提供了初始化列表的机制
如果在函数体内部,给成员属性赋值,是一个赋值的过程,不是初始化的过程
类名():成员属性1(形参的值1),成员属性2(形参的值)`````
在函数头后面,使用:引出初始化列表,每个成员属性以,分隔,()里面是形参,外面是成员属性
iii)需要使用初始化列表的情况
- 形参和成员属性同名
- 类中有引用成员时,必须使用初始化列表
#include <iostream>
using namespace std;
class Stu
{
int age;
float high;
int &score;
public:
//构造函数支持函数重载
Stu(int age,float high,int a):age(age),high(high),score(a)
{
cout << "Stu的有参构造" << endl;
}
};
int main()
{
//Stu s;
int n1 = 90;
Stu s1(18,9,n1); //在栈区
Stu *p;
//p = new Stu; //在堆区申请了Stu类对象的空间
return 0;
}
- 类中有const修饰的成员时,必须使用初始化列表
#include <iostream>
using namespace std;
class Stu
{
int age;
float high;
const int score;
public:
// Stu()
// {
// cout << "Stu的无参构造函数" << endl;
// }
//构造函数支持函数重载
Stu(int age,float high,int a):age(age),high(high),score(a)
{
cout << "Stu的有参构造" << endl;
}
};
int main()
{
//Stu s;
int n1 = 90;
Stu s1(18,9,89); //在栈区
Stu *p;
//p = new Stu; //在堆区申请了Stu类对象的空间
return 0;
}
- 类中含有其他类的子对象时,必须使用初始化列表(类的包含关系)
(如果另一个类只有有参构造需要在初始化列表中宏显性调用,如果另一个类有无参构造,可以不写初始化列表)
类的包含关系
#include <iostream>
using namespace std;
class Per
{
string name;
public:
Per(string name)
{
this->name = name;
cout << "Per的有参构造" << endl;
}
};
class Stu
{
int age;
float high;
int score;
Per p1; //Per类只有有参构造函数
public:
Stu():p1("zhangsan")
{
cout << "Stu的无参构造函数" << endl;
}
//构造函数支持函数重载
Stu(int age,float high,int a,string name):p1(name)
{
this->age = age;
this->high = high;
this->score = a;
cout << "Stu的有参构造" << endl;
}
};
int main()
{
Stu s;
int n1 = 90;
Stu s1(18,9,89,"lisi"); //在栈区
return 0;
}
【6】析构函数
不支持函数重载
在类对象空间消亡时,系统自动调用
i)格式
~类名()
{
//函数体
}
ii)调用时机
栈区:对象消亡时,自动调用
堆区:什么时候delete,什么时候调用
构造函数和析构函数调用的时机:
先构造的后析构,后构造的先析构
iii)需要显性写出析构函数的场景
类中有指针成员,并且指针成员,指向堆区的空间
#include <iostream>
using namespace std;
class Stu
{
string name;
int *p;
public:
Stu():p(new int) //保证指针成员,指向堆区的空间
{
cout << "堆区申请的空间为:" << p << endl;
cout << "Stu的无参构造" << endl;
}
// Stu(string name,int p):name(name),p(new int(p))
// {
// cout << "Stu的有参构造" << endl;
// }
~Stu()
{
cout << "准备释放堆区的空间:" << p << endl;
delete p;
cout << "Stu的析构函数" << endl;
}
};
int main()
{
Stu s1;
return 0;
}
【7】拷贝构造函数
利用一个类对象,给另一个类对象初始化时,自动调用拷贝构造函数
如果自己实现了拷贝构造,系统不再提供默认的拷贝构造
i)格式
类名(同类对象的引用)
{
//函数体
}
ii)使用
#include <iostream>
using namespace std;
class Per
{
string name;
public:
//Per中,自己定义了有参构造,系统不再提供无参构造
Per(string name)
{
this->name = name;
cout << "Per的有参构造" << endl;
}
~Per()
{
cout << "Per的析构函数" << endl;
}
};
class Stu
{
int age;
float high;
int score;
//Per p1; //Per类只有有参构造函数
public:
Stu()
{
cout << "Stu的无参构造函数" << endl;
}
//构造函数支持函数重载
Stu(int age,float high)
{
this->age = age;
this->high = high;
cout << "Stu的有参构造" << endl;
}
//拷贝构造
Stu(Stu &other)
{
//this->age = other.age;
this->high = other.high;
cout << "拷贝构造函数" << endl;
}
~Stu()
{
cout << "Stu的析构函数" << endl;
}
void show();
};
void Stu::show()
{
cout << "age = " << age << endl;
cout << "high = " << high << endl;
}
int main()
{
Stu s1(19,100);
cout << "s1的show:" << endl;
s1.show();
Stu s2=s1;
cout << "s2的show:" << endl;
s2.show();
return 0;
}
iii)深浅拷贝问题**
当类中有指针成员,会涉及到深浅拷贝问题
浅拷贝:两个不同类对象的指针成员,指向同一片空间
问题:析构时,会发生二次释放问题;同一片空间被两个不同的类对象占用,发生资源抢占
深拷贝:两个不同类对象的指针成员,指向不同的空间,但是保存的是同样的数据
浅拷贝
深拷贝
#include <iostream>
using namespace std;
class Stu
{
string name;
int *p;
public:
Stu():p(new int) //保证指针成员,指向堆区的空间
{
cout << "堆区申请的空间为:" << p << endl;
cout << "Stu的无参构造" << endl;
}
Stu(string name,int p):name(name),p(new int(p))
{
}
~Stu()
{
cout << "准备释放堆区的空间:" << p << endl;
delete p;
cout << "Stu的析构函数" << endl;
}
//使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
Stu(Stu &other):name(other.name),p(new int(*(other.p)))
{
/*this->name = other.name;
this->p = new int(*(other.p));*/
cout << "Stu的拷贝构造" << endl;
}
void show();
};
void Stu::show()
{
cout << "name= " << name << endl;
cout << "p= " << p << endl;
cout << "*p= " << *p << endl;
}
int main()
{
Stu s1("zhangsan",18);
cout << "s1的show:" << endl;
s1.show();
Stu s2 = s1;
cout << "s2的show:" << endl;
s2.show();
return 0;
}
iv)拷贝构造函数的调用时机
- 使用已有的类对象,给新的类对象初始化
- 函数的参数是一个类对象时,也会调用拷贝构造函数
- 函数的返回值是一个类对象时,也会调用拷贝构造函数
测试代码:
#include <iostream>
using namespace std;
class Stu
{
string name;
public:
Stu()
{
cout << "Stu的无参构造" << endl;
}
Stu(Stu &other):name(other.name)
{
cout << "Stu的拷贝构造函数" << endl;
}
Stu(string name):name(name)
{
cout << "Stu的右参构造" << endl;
}
};
Stu fun(Stu s1)
{
return s1;
}
int main()
{
Stu s;
//Stu s2(fun(s)); //会报错,因为fun(s)的返回值是一个临时值,不能引用
return 0;
}
【8】拷贝赋值函数
使用已有的类对象,给另外一个已有的类对象赋值
系统默认提供一个拷贝赋值函数
本质:赋值运算符的重载
i)格式
类名 &operator=(const 类名&other)
{
//函数体
}
ii)代码
#include <iostream>
using namespace std;
class Stu
{
string name;
int *p;
public:
//深拷贝赋值函数
Stu &operator=(const Stu &other)
{
name = other.name;
*p = *(other.p);
cout << "Stu的拷贝赋值函数" << endl;
return *this;
}
Stu():p(new int) //保证指针成员,指向堆区的空间
{
cout << "堆区申请的空间为:" << p << endl;
cout << "Stu的无参构造" << endl;
}
Stu(string name,int p):name(name),p(new int(p))
{
}
~Stu()
{
cout << "准备释放堆区的空间:" << p << endl;
delete p;
cout << "Stu的析构函数" << endl;
}
//使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
Stu(Stu &other):name(other.name),p(new int(*(other.p)))
{
/*this->name = other.name;
this->p = new int(*(other.p));*/
cout << "Stu的拷贝构造" << endl;
}
void show();
};
void Stu::show()
{
cout << "name= " << name << endl;
cout << "p= " << p << endl;
cout << "*p= " << *p << endl;
}
int main()
{
Stu s1("zhangsan",18);
cout << "s1的show:" << endl;
s1.show();
Stu s2;
s2 = s1;
cout << "s2的show:" << endl;
s2.show();
return 0;
}
【9】匿名对象
没有对象名,通过类名实例化出来的对象,类名();
Stu();生命周期更短
- 全局函数传参
- 类数组赋值 //int a=9,b=7,c=8; int arr[3]={a,b,c}; //int arr[3]={9,7,8};
- 临时调用类中的成员函数
- 给新的类对象赋值
#include <iostream>
using namespace std;
class Stu
{
string name;
int *p;
public:
//深拷贝赋值函数
Stu &operator=(const Stu &other)
{
name = other.name;
*p = *(other.p);
cout << "Stu的拷贝赋值函数" << endl;
return *this;
}
Stu():p(new int) //保证指针成员,指向堆区的空间
{
cout << "堆区申请的空间为:" << p << endl;
cout << "Stu的无参构造" << endl;
}
Stu(string name,int p):name(name),p(new int(p))
{
}
~Stu()
{
cout << "准备释放堆区的空间:" << p << endl;
delete p;
cout << "Stu的析构函数" << endl;
}
//使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
Stu(const Stu &other):name(other.name),p(new int(*(other.p)))
{
/*this->name = other.name;
this->p = new int(*(other.p));*/
cout << "Stu的拷贝构造" << endl;
}
void show();
};
void Stu::show()
{
cout << "name= " << name << endl;
cout << "p= " << p << endl;
cout << "*p= " << *p << endl;
}
void fun(Stu s1)
{
cout << "调用成功" << endl;
}
int main()
{
//1、使用匿名对象用做全局函数传参
fun(Stu()); //匿名对象的生命周期,只在定义语句的位置,是一个临时值
//2、想要临时使用类中的成员函数
Stu().show();
//3、给类对象的数组赋值
Stu arr[3]={Stu("zhangsan",8),Stu("lisi",19),Stu("xiaoming",20)};
//4、给新的类对象赋值
Stu s3(Stu("zhangsan",18));
return 0;
}
【10】C++中结构体和C的区别以及C++中结构体和类的区别
- C中定义需要加struct,C++中可以不加struct
- C++中结构体可以有访问权限的控制(public、private、protected)
- C++中结构体可以继承
- C++中结构体可以封装函数
- C++中结构体内可以定义另外一个结构体声明(类型)
结构体和类的区别:
- 使用场合不同,类适用于某一类对象属性和方法的封装,结构体用于某种数据结构的实现
- 类中默认private,结构体中默认是public
- 类默认是私有继承,结构体默认是共有继承
- 类的封装性比结构体的封装性更好