个人学习C++笔记5

143 阅读3分钟

C++核心编程

4.面向对象

C++面向对象的三大特性:封装,继承,多态
C++认为万事万物都皆为对象,对象上有其属性和行为

image.png

4.1封装

4.1.1封装的意义

封装是C++三大特性之一 封装的意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装的意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法:class 类名{访问权限:属性/行为};
封装的意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制。
访问权限有三种:

  1. public 公共权限 成员类内可以访问 类外可以访问
  2. protected 保护权限 成员类内可以访问 类外不可以访问
  3. private 私有权限 成员类内可以访问 类外不可以访问

4.1.2struct和class的区别

在C++中struct和class唯一的区别就在于默认的访问权限不同
区别:

  • struct的默认权限为公共
  • class的默认权限为私有

4.1.3成员属性设置为私有

优点:

  • 将所有成员属性设置为私有,可以自己控制读写权限
  • 对于写权限,我们可以检测数据的有效性

4.2对象的初始化和清理

C++中面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置

4.2.1 构造函数和析构函数

image.png

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名和类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象的时候会自动调用构造,无需手动调用,而且只会调用一次

析构函数语法:~类名(){}

  1. 析构函数,没有返回值也不写void

  2. 函数名称与类名相同,在名称前加符号~

  3. 析构函数不可以有参数,因此不可以发生重载

  4. 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次

构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构。

4.2.2 构造函数的分类及调用

两种分类方式:

  • 按参数分为:有参构造和无参构造
  • 按类型分为:普通构造和拷贝构造
class person
{
public:
	//构造函数
	person()
	{
		cout << "person的无参构造函数" << endl;
	}
	person(int a)
	{
		age = a;
		cout << "person的有参构造函数" << endl;
	}
	//拷贝构造函数
	person(const person &p)
	{
		age = p.age;
		cout << "person的拷贝构造函数" << endl;
	}
	//析构函数
	~person()
	{
		cout << "person的析构函数" << endl;
	}
	int age;
};

C++

三种调用方式:

  • 括号法
//括号法
	person p1;//默认构造函数
	person p2(10);//有参构造函数
	person p3(p1);//构造函数
        
        person p4();//这种是错误的

C++

注意事项:
调用默认构造函数时候,不要加(),因为编译器会认为这是一个函数的声明,不会认为在创建对象。

  • 显示法
 //显示法
	person p1;//默认构造函数
	person p2 = person(10);//有参构造函数
	person p3 = person(p2);//拷贝构造函数
        
        person (10);//匿名对象
        //特点:当前行执行结束后,系统会立即回收掉匿名对象
C++

注意事项: 匿名对象所在行执行结束后,系统会立即回收掉匿名对象。 不要利用拷贝构造函数初始化匿名对象。

person(p3);

编译器会认为person(p3)===person p3;对象声明

会报错显示重定义

  • 隐式转换法
// 隐式转换法
	person p1 = 10;//有参构造 相当于 person p1=person(10);
	person p2 = p1;//拷贝构造

C++

4.2.3 拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新的对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

4.2.4 构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,C++不会再提供其他构造函数

4.2.5 深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

4.2.6 初始化列表

作用:

C++提供了初始化列表语法,用来初始化属性

语法:

构造函数():属性1(值1),属性2(值2)...{}

4.2.7 类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

当其他类对象作为本类的成员,构造的时候先构造类对象,再构造自身

析构的顺序相反(先进后出)

4.2.8 静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

  • 静态成员变量

所有对象共享一份数据

在编译阶段分配内存

类内声明,类外初始化

  • 静态成员函数

所有对象共享一个函数

静态成员函数只能访问静态成员变量

(无法区分是哪个对象的)

静态成员变量 不属于某个对象上,所有对象都共享同一份数据

因此静态成员变量有两种访问方式

image.png

静态成员变量也有访问权限

静态成员函数也有访问权限

4.3 C++对象模型和this指针

4.3.1 成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

空对象占用内存空间为: 1

C++编译器会给每个对象也分配一个字节空间,是为了区分空对象占内存的位置,每个空对象也应该有独一无二的内存地址

image.png

4.3.2 this指针概念

image.png 通过4.3.1我们知道C++中成员变量和成员函数是分开存储的

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的那?

C++通过提供特殊的对象指针,this指针,解决上述问题

4.3.3空指针访问成员函数

C++中空指针也可以调用成员函数,但是要注意有没有用到this指针

如果用到this指针,需要加判断保证代码的健壮性

image.png 如果为NULL,直接returan

4.3.4const修饰成员函数

常函数:

  • 成员函数后加const,我们称这个函数为常函数
    
  • 常函数不可以修改成员属性
    
  • 成语属性声明时加关键字mutable后,在常函数中依然可以修改
    

常对象:

  • 声明对象前加const称该对象为常对象
    
  • 常对象只能调用常函数
    

image.png

image.png

4.4友元

image.png 友元让类外的函数或者类进行访问私有属性

友元的三种实现:

4.4.1

  •  全局函数做友元
    
#include <iostream>
#include<string>
using namespace std;

class Building
{
    friend void GoodGay(Building* building);
public:
    string m_Sittingroom;
public:
    Building()
    {
        m_Beedroom = "卧室";
        m_Sittingroom = "客厅";
    }

private:
    string m_Beedroom;

};

void GoodGay(Building *building)
{
    cout << "好基友正在访问:" <<building->m_Sittingroom<< endl;
    cout << "好基友正在访问:" << building->m_Beedroom<< endl;
}

void test()
{
    Building building;
    GoodGay(&building);
}

int main()
{
    test();
    system("pause");
    return 0;
}


C++

4.4.2

  •  类做友元
    

#include <iostream>
#include<string>
using namespace std;

class Building
{
    friend class GoodGay;
public:
    Building()
    {
        Sitingroom = "客厅";

        Bedroom = "卧室";
    }
public:
    string Sitingroom;
private:
    string Bedroom; 
};

class GoodGay
{
    Building building;
public:
    void visit();
};

void GoodGay::visit()
{
    Building building;
    cout << "好基友正在访问:" <<building.Sitingroom << endl;
    cout << "好基友正在访问:" << building.Bedroom<< endl;
}

void test()
{
    GoodGay gg;
     gg.visit();
}

int main()
{
    test();
    system("pause");
    return 0; 
}
C++

4.4.3

  •  成员函数做友元
    


#include <iostream>
#include<string>
using namespace std;


class Building
{
public:
    Building()
    {
        Sitingroom = "客厅";

        Bedroom = "卧室";
    }

    string Sitingroom;

    string Bedroom;

};


class GoodGay
{
public:
    Building* building;

public:
    GoodGay()
    {
        building = new Building;
    }

public:
    void visit();
    void visit2();

};

//类外实现函数
void GoodGay::visit()
{
  
    cout << "好基友正在访问:" << building->Sitingroom<<endl;
}

void GoodGay::visit2()
{
   
    cout << "好基友正在访问:" <<building->Bedroom<< endl;
}

void test()
{
    GoodGay gg;
    gg.visit();
    gg.visit2();
}

int main()
{
    test();
    system("pause");
    return 0;
}

C++

4.5运算符重载

运算符重载:对已有的运算符重新定义,赋予其另一种功能,以适应不同的数据类型

4.5.1加号运算符重载

实现两个自定义数据类型相加的运算

#include <iostream>
#include<string>
using namespace std;

class Person
{
public:
    int m_A;
    int m_B;
    //成员函数重载
 /*   Person operator+ (Person& p)
    {
        Person temp;
        temp.m_A = this->m_A + p.m_A;
        temp.m_B = this->m_B + p.m_B;
        return temp;
    }*/

};

//全局函数重载
Person operator+(Person& a,Person&b)
{
    Person temp;
    temp.m_A = a.m_A + b.m_A;
    temp.m_B = a.m_B + b.m_B;
    return temp;
}

void Sit()
{
    Person p1;
    p1.m_A = 10;
    p1.m_B = 20;

    Person p2;
    p2.m_A = 30;
    p2.m_B = 40;

    Person p3;
    p3 = p1 + p2;
    cout << "p3的m_A的值为:" << p3.m_A << endl;
    cout << "p3的m_B的值为:" << p3.m_B << endl;

}


int main()
{
    
    Sit();
    system("pause");
    return 0;
}

C++

总结1. 对于内置的数据类型的表达式的运算符是不可改变的

1+1=2
不可以改为1+1=0

总结2.不要滥用运算符重载

在上面的示例中Person operator+不可以改为两个对象数据相减

4.5.2

4.6继承

4.7多态

5.文件操作