3.C++类和对象 封装
类和对象的创建与使用
class Student{
public:
string name;
float score;
string getName(){
return name;
}
float getscore(){
return score;
}
void setName(string name){
this->name = name;
}
void setScore(float score){
this->score = score;
}
}
int main(){
Student s1;
s1.name = "lry";
s1.score = 91.5;
s1.setScore(99);
}
类和对象的访问权限
- public 类内可以访问,类外可以
- protected 类内可以访问,类外不可以,但是子类可以访问
- private 类内可以访问,类外不可以
类和结构体的唯一区别就在于默认访问权限不同,class为私有,struct为公有
构造函数和析构函数
构造和析构必须有,如果不提供,则编译器自动补上空实现。
编译器还会提供一个拷贝构造函数,默认进行值拷贝
构造函数
- 构造函数可以有参数
- 可以发生重载
- 名称与类名相同
- 无返回值和void
- 创建实例化对象会自动调用
析构函数
和构造函数类似,但不可以有参数,不能重载,名称前加~
class Student{
public:
string name;
float score;
Student(float score){ //构造函数1
this->score = score;
}
Student(string name,float score){ //构造函数2
this->name = name;
this->score = score;
}
~Student(){ //析构函数
//函数体
}
}
普通构造和拷贝构造
class Student{
public:
string name;
float score;
Student(string name,float score){ //普通构造函数
this->name = name;
this->score = score;
}
Student(const Student &p){ //拷贝构造函数
this->name = name;
this->score = score;
}
}
拷贝构造不能修改传进来的参数,所以要用const,同时为了避免内存消耗,故用引用避免再实例化p一个对象然后进行传值。
不能用拷贝构造函数初始化匿名对象
Person (p3)
编译器会把括号去掉,等价于Person p3
报错:重定义
拷贝构造函数调用时机
-
使用已有对象初始化新对象
Person p2(p1)
-
值传递方式给参数传值
void test(Person p1)
-
以值方式返回局部对象
Person p1(func())
( func返回一个Person对象 )
深拷贝与浅拷贝
-
浅拷贝:简单的复制拷贝操作,在堆区的内存会重复释放
-
深拷贝:在堆区重新申请空间,进行拷贝操作
class Student{
public:
int a;
int *p;
Student(){ //普通构造函数
this->a=10;
this->p = new int(4);
}
Student(const student &s){ //普通拷贝构造函数
this->a= s.a;
this->p = s.p; //堆区指向内存地址相同
}
Student(const student &s){ //自己实现的深拷贝构造函数
this->a= s.a;
this->p = new int(*s.p); //重新在堆区开辟一片内存空间并进行赋值
}
~Student(){
if(p!=NULL){
delete p; //如果同时实例化两个对象,那么析构的时候,第二个对象析构函数会造成堆区内存重复释放
p=NULL;
}
}
}
int main(){
Student p1 = Student();
Student p2(p1);
}
构造函数调用方式和时机
int main(){
//三种调用方式
Student s1;
Student s1 = Student();//右侧的叫做匿名对象
Student();//匿名对象 当前行结束后 系统会立即回收掉匿名对象
Student s1 = 10; //相当于 Student s1 = Student(10); 有参构造
Student s3 = s4; //相当于 Student s1 = Student(s4); 拷贝构造
}
顺序创建的对象,构造和析构遵从先进后出原则。
类对象作为类成员时,先调用对象成员的构造,再调用本类构造,析构相反。
初始化列表
注意冒号位置
class Student{
public:
int a;
int b;
int c;
Student():name("li"),score(99){ //固定初始化列表
this->score = score;
}
Student(int a,int b,int c):this.a(a),this.b(b),this.c(c){ //固定初始化列表
//等价于
//this->a = a;
//this->b = b;
//this->c = c;
}
}
静态成员
- 静态成员变量
static int count;
- 所有对象共享同一份数据(别人改了自己也会改)
- 编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 共享同一个函数
- 只能访问静态成员变量
class Student{
public:
static int m_A; //类内声明
static void func(){
m_A =100;
}
}
int Student::m_A=100; //类外初始化
int main(){
Student p1 = Student();
Student p2(p1);
}
this指针
this指向被调用的成员函数所属对象
本质是指针常量,指向是不可以修改的
空指针调用成员函数
对象类型的空指针可以调用该对象成员函数,但仅限于没有成员变量的类似输出操作。
class Student{
public:
int age;
void func1(){
cout<<"hello!";
}
void func2(){
// if(this==NULL){ 应该加这一段代码防止出现空指针调用成员函数问题
// return
//}
cout<<age;
}
}
int main(){
Student *p1 = NULL;
p1.func1();//成功
p1.func2();//失败
}
const修饰成员函数(常函数)
-
常函数
-
常函数后加const
-
常函数不能修改成员变量
-
属性声明加mutable,常函数依然可以修改
-
-
常对象
- 常对象只能调用常函数
- 声明对象前加const
- 可以修改静态变量的值
class Student{
public:
int A;
mutable int B;
void func1() const{ //const修饰的是this指针(指针常量) 变为常量指针常量 值也不能改了
A = 100; //报错 不可以修改A
B = 100; //不报错,可以修改
}
}
void test2(){
const Student B;//常对象
B.A = 100; //报错
B.B = 100; //可以
B.func1();//常对象只能调用常函数
}
友元
全局函数做友元
全局函数可以访问一个类的私有成员
class Building{
friend void goodgay(Building &b); //声明友元全局函数
public:
int livingroom;
private:
int bedroom;
}
void stranger(Building &b){
b.bedroom=10;//修改失败
}
void goodgay(Building &b){
b.bedroom=10;//修改成功
}
类做友元
一个类可以访问另一个类的私有成员
类内加上friend class Student
成员函数做做友元
成员函数可以访问另一个类的私有成员
运算符重载
加号重载
对已有运算符重新定义,赋予其另一种功能。
class Person{
int A,B;
Person operator+ (Person &p){ //成员函数来运算符重载 p = p1.operator+(p2) 简化为p=p1+p2
Person temp;
temp.A = this->A + p.A;
temp.B = this->B + p.B;
return temp;
}
}
Person operator+(Person &p1,Person &p2){ //全局函数重载
Person temp;
temp.A = p1.A + p2.A;
temp.B = p1.B + p2.B;
return temp;
}
左移运算符重载(输出重载)
class Person{
int A,B;
void operator<< (cout){ //成员函数来运算符重载 p1.operator<<(cout) 简化为p1<<cout 与cout<<p1相反,因此一般不用成员函数重载输出
}
}
//全局函数重载<< 简化为cout<<p 但是要返回cout
//cout全局只能有一个,用引用 同时返回cout,注意语法糖
ostream operator<<(ostream &cout,Person &p){
cout<<p.A = 10;
cout<<p.B = 10;
return cout;
}