《21天学通C++》(第九章)类和对象(2)

1.析构函数

析构函数在对象被销毁时自动 被调用,使用~表示,析构函数用于释放资源、销毁对象,示例代码如下:

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;
//封装一个字符缓冲区
//并展示了如何使用构造函数和析构函数来管理动态分配该缓冲区的内存
class MyString
{
private:
    char* buffer;//字符缓冲区
    
public:
    MyString(const char* initString){
        if(initString!=NULL){//如果初始参数不为空,则分配内存创建缓冲区
            buffer=new char[strlen(initString)+1];
            //在C++中,字符串是以空字符 '\0' 结尾的字符数组。
            //用 strlen 函数时,它会计算字符串中直到遇到第一个空字符 '\0' 为止的字符数量,但不包括空字符本身
            //所以为了保证内存分配成功,必须要加1
            strcpy(buffer,initString);
            //把数据复制给缓冲区
        }else{
            buffer=NULL;
        }
    }

    ~MyString(){//结束后释放掉缓冲区buffer的内存,会被自动调用
        cout<<"clearing up"<<endl;
        if(buffer!=NULL){
            delete[] buffer;
        }
    }

    int getLength(){
        return strlen(buffer);
    }

    const char* getString(){
        return buffer;
    }
};

int main(){
    MyString sayHello("Hello!");
    cout<<"buffer is "<<sayHello.getLength()<<endl;
    cout<<"buffer contains is "<<sayHello.getString()<<endl;
    
    system("pause");
    return 0;
}

2.复制构造函数

(1) 浅复制及其存在的问题

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;
class MyString
{
private:
    char* buffer;
    
public:
    MyString(const char* initString){
        buffer=NULL;
        if(initString!=NULL){
            buffer=new char[strlen(initString)+1];
            strcpy(buffer,initString);
        }
    }

    ~MyString(){
        cout<<"clearing up"<<endl;
        delete[] buffer;
        }
    
    int getLength(){
        return strlen(buffer);
    }

    const char* getString(){
        return buffer;
    }
};

void useMyString(MyString str){
    cout<<"str buffer length is "<<str.getLength()<<endl;
    cout<<"str buffer contains is "<<str.getString()<<endl;
    return;
}

int main(){
    MyString sayHello("Hello!");//创建对象sayHello
    useMyString(sayHello);
    //这里是将sayHello的指针值(内存地址)复制给useMyString里面的str
    //所以导致sayHello.buffer和str.buffer指向了同一个内存单元
    //当调用完之后,~MyString()会释放掉buffer
    cout<<"buffer contains"<<sayHello.getString()<<endl;
    //这里调用sayHello.getString()就不会正确输出,甚至程序崩溃
    system("pause");
    return 0;
}

上述代码中的复制过程就叫浅复制,即对于动态分配的内存或指向动态内存的指针成员,则仅仅复制指针的值(内存地址),而不是复制指针指向的内存内容

(2) 使用复制构造函数确保深复制

复制构造函数主要通过对函数进行重载来实现的,对上述代码进行修改,修改后如下:

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;
class MyString
{
private:
    char* buffer;
    
public:
    MyString(const char* initString){
        buffer=NULL;
        if(initString!=NULL){
            buffer=new char[strlen(initString)+1];
            strcpy(buffer,initString);
        }
    }

    //深复制、重载
    MyString(const MyString& copySource){//采用引用获取原参数的内容
        buffer=NULL;
        if(copySource.buffer!=NULL){
            buffer=new char[strlen(copySource.buffer)+1];//为新对象分配内存
            strcpy(buffer,copySource.buffer);// 将copySource的buffer内容复制到新分配的内存中
        }
    }

    ~MyString(){
        cout<<"clearing up"<<endl;
        delete[] buffer;
        }
    
    int getLength(){
        return strlen(buffer);
    }

    const char* getString(){
        return buffer;
    }
};

void useMyString(MyString str){
    cout<<"str buffer length is "<<str.getLength()<<endl;
    cout<<"str buffer contains is "<<str.getString()<<endl;
    return;
}

int main(){
    MyString sayHello("Hello!");
    cout<<"raw buffer contains "<<sayHello.getString()<<endl;
    useMyString(sayHello);
    //深复制后sayHello.buffer和str.buffer就不会指向同一个内存单元了
    //调用函数后,销毁的是str.buffer
    cout<<"now buffer contains "<<sayHello.getString()<<endl;
    //所以再次调用sayHello.buffer能够正常输出
    system("pause");
    return 0;
}

3.移动构造函数

(1)移动构造函数是C++11引入的一种特殊的构造函数,它允许在创建对象时,将一个已存在对象的资源转移 给新创建的对象,而不是复制资源

(2)在某些情况下,为了避免不必要的资源复制,提升程序效率,才会使用移动构造函数

4.构造函数和析构函数的其他用法

(1)通过声明私有的复制构造函数,禁止类型被复制

(2)通过关键词static确保只能有一个实例

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;
class President
{
private:
    President(){};// 私有构造函数,防止外部直接创建对象
    President(const President&);// 私有拷贝构造函数,防止复制对象
    const President& operator=(const President&);// 私有赋值运算符,防止对象赋值

    string name;

public:
    static President& getInstance(){
    	// 局部静态变量,确保只创建一次实例
        static President onlyInstance;
        return onlyInstance;

    }
    
    string getName(){
        return name;
    }

    void setName(string inputName){
        name=inputName;
    }
};


int main(){
    President& firstPresident=President::getInstance();//单例模式常用方法
	//调用President类的getInstance()静态方法。
	//getInstance()方法返回President类的唯一实例的引用。
	//将这个引用赋值给名为firstPresident的引用变量。
	//这样,firstPresident就成为了President类唯一实例的一个引用
    firstPresident.setName("LiHua");
    cout<<"name is "<<firstPresident.getName()<<endl;

    system("pause");
    return 0;
}

PS: 为了方便扩充应用程序的功能,仅在绝对必要的情况下才使用单例模式

5.使用构造函数进行类型转换

cpp 复制代码
#include <iostream>
class Length {
private:
    double meters; // 以米为单位存储长度
public:
    // 默认构造函数
    Length() : meters(0) {}

    // 转换构造函数,将double类型的值转换为Length对象
    Length(double value) : meters(value) {}

    // 成员函数,返回以米为单位的长度
    double getMeters() const {
        return meters;
    }
};

int main() {
    // 使用默认构造函数创建Length对象
    Length len1;
    // 使用转换构造函数创建Length对象,隐式地将double转换为Length类型
    Length len2(100.0);
    return 0;
}

隐式转换有时会存在一些问题,所以会用explicit来避免无意识的隐式转换。

6.this指针

this包含当前对象的地址,在类成员方法中调用其他成员方法时,编译器将隐式的传递this指针

7.结构和类不同的地方

默认情况下,结构中的成员默认为公有 ,以公有方式继承 基结构

默认情况下,类成员默认为私有 ,以私有方式继承

8.声明友元

允许友元类友元函数外部 访问私有数据成员和方法

cpp 复制代码
#include <iostream>
using namespace std;

class Person {
private:
    int health; // 表示健康状态

public:
    Person(int h) : health(h) {} // 构造函数设置健康状态

    // 声明Doctor类为友元类
    friend class Doctor;

    // 声明一个友元函数,它不是类的成员,但可以访问Person的私有成员
    friend void checkHealth(Person& p);
};

//Doctor类则被声明为Person类的友元类,这样Doctor类的所有成员函数都可以访问Person类的私有成员
class Doctor {
public:
    void diagnose(Person& patient) {
        if (patient.health < 90) {
            cout << "The patient is not in good health." << endl;
        } else {
            cout << "The patient is in excellent health." << endl;
        }
    }
};

// 友元函数的实现
void checkHealth(Person& p) {
    cout << "Checking health status: " << p.health << endl;
}

int main() {
    Person patient(80); // 创建一个健康状态为80的Person对象
    Doctor dr; // 创建一个Doctor对象

    // Doctor对象可以访问Person对象的私有成员
    dr.diagnose(patient);

    // 友元函数也可以访问Person对象的私有成员
    checkHealth(patient);

    system("pause");
    return 0;
}

9.共用体

使用关键字union,示例如下:

cpp 复制代码
union unionName{
	string a;
	string b;
	string c;
}
//实例化对象并使用
unionName unObject;
unObject.a="a1";
unObject.b="b2";

PS: 共用体不能继承

10.对类和结构使用聚合初始化

使用花括号 {}初始化成员

cpp 复制代码
MyStruct ms = { 1, 2.0 };

或者

cpp 复制代码
struct test{
	int num;
	double pi;
}

//初始化
test a1{2017,3.14}

或者

cpp 复制代码
struct test2{
	int num;
	char h[5];
	int q[6];
}

//初始化
test a1{2017,['a','b','c','d','\0'},[1,2,3,4,5,6]};
相关推荐
千穹凌帝1 分钟前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
AlexMercer10123 分钟前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
friklogff4 分钟前
【无标题】云端之C#:全面解析6种云服务提供商的SDK
开发语言·flask·c#
Reese_Cool17 分钟前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言
海里真的有鱼18 分钟前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
zxctsclrjjjcph34 分钟前
【C语言】常见的C语言概念
c语言·开发语言
小灰灰爱代码39 分钟前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
Eiceblue1 小时前
Python 复制Excel 中的行、列、单元格
开发语言·python·excel
项目題供诗1 小时前
尚品汇-秒杀商品存入缓存、Redis发布订阅实现状态位(五十一)
开发语言·php
m0_714590261 小时前
汇编(实现C语言程序的调用)
c语言·开发语言·汇编