《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]};
相关推荐
无尽的大道8 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒11 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~15 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
binishuaio20 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE22 分钟前
【Java SE】StringBuffer
java·开发语言
就是有点傻26 分钟前
WPF中的依赖属性
开发语言·wpf
洋24035 分钟前
C语言常用标准库函数
c语言·开发语言
进击的六角龙36 分钟前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel
wrx繁星点点38 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
NoneCoder1 小时前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发