自学内容网 自学内容网

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

1.析构函数

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

#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) 浅复制及其存在的问题

#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) 使用复制构造函数确保深复制
复制构造函数主要通过对函数进行重载来实现的,对上述代码进行修改,修改后如下:

#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确保只能有一个实例

#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.使用构造函数进行类型转换

#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.声明友元

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

#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,示例如下:

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

PS: 共用体不能继承

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

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

MyStruct ms = { 1, 2.0 };

或者

struct test{
int num;
double pi;
}

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

或者

struct test2{
int num;
char h[5];
int q[6];
}

//初始化
test a1{2017,['a','b','c','d','\0'},[1,2,3,4,56]};

原文地址:https://blog.csdn.net/m0_53115174/article/details/137876054

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!