[NKU]C++基础课(三)析构函数、拷贝构造函数、浅拷贝、友元、static修饰类的成员、const修饰类的成员、深拷贝、友元函数、友元类、运算符重载的语法、规则

一、析构函数
清理对象占用资源
如使用 newnew[] 分配的内存,用deletedelete[] 删除动态分配的对象
delete[] _food;

构造函数(Constructor)是C++类的一个特殊成员函数,它的主要作用是初始化对象的状态,确保对象在创建时处于一个有效的初始状态。
构造函数的名称与类名相同,并且没有返回值。

析构函数(Destructor)是C++类的一个特殊成员函数,它的主要作用是在对象生命周期结束时清理对象占用的资源,确保程序的正确性和安全性。

动态分配的对象是指使用 newnew[] 运算符在堆(Heap)上分配内存并创建的对象。
与栈(Stack)上分配的局部对象不同,动态分配的对象的生命周期不由作用域控制,而是由程序员显式管理
这意味着你需要手动使用 deletedelete[] 来释放这些对象占用的内存,以避免内存泄漏。

析构函数声明
~Rabbit()
类外定义析构函数

Rabbit::~Rabbit(){}
Rabbit::~Rabbit(){
        cout <<""<<endl;
        delete[] p;     
}

语法结构

类名::~类名() {
    // 析构函数体
}

#define _CRT_SECURE_NO_WARNINGS
// 是一个预处理器指令,用于禁用与不安全的C运行时函数相关的警告。虽然它可以减少编译器警告,但建议谨慎使用,优先考虑使用安全版本的函数以避免潜在的安全问题。
#include <cstring> // 需要包含cstring头文件,用于memset和strcpy
#include <iostream>
using namespace std;

class Rabbit{
public:
    Rabbit(string name, const char* pf); //构造函数 声明
    void eat(); //成员函数
    ~Rabbit();  //析构函数 声明
private:
    string _name;//成变
    char* _food;
};

//类外定义构造函数
Rabbit::Rabbit(string name, const char* pf){
    cout << "调用构造函数" << endl; 
    _name = name; //初始化成员变量
    _food = new char[50];  // 分配动态内存
    memset(_food, 0, 50);  //将名为_food的内存区域的前50个字节全部设置为0
    strcpy(_food, pf);  // 赋值字符串  pf 指向的字符串复制到 _food 指向的内存中
}

//类外定义成员函数
void Rabbit::eat(){
    cout << _name << "is eating" << _food << endl;
}

//类外定义析构函数
Rabbit::~Rabbit(){
    cout << "调用析构函数" << _name << endl;
    if (_food!=NULL)
        delete[] _food;
}


int main(){
    Rabbit A("A", "luobo");
    A.eat();
    Rabbit B("B", "baicai");
    B.eat();
    return 0;
}

输出

二、拷贝构造函数

拷贝构造函数(Copy Constructor)是C++中的一个特殊构造函数,
用于创建一个新对象作为现有对象的副本。
它的语法结构通常如下:

类名(const 类名& 参数名);

class(const className& other);
#include<iostream>
#include<string>
using namespace std;

class Sheep{
public:
    Sheep(string name, string color);
    Sheep(const Sheep& other);
    void show() const; // 添加了 const 修饰符,表示该方法不会修改对象状态
    ~Sheep();
private:
    string _name;
    string _color;
};

// 构造函数
Sheep::Sheep(string name, string color){
    cout << "调用构造函数" << endl;
    _name = name;
    _color = color;
}

// 拷贝构造函数
Sheep::Sheep(const Sheep& another){
    cout << "调用拷贝构造函数" << endl;
    _name = another._name;
    _color = another._color;
}

void Sheep::show() const {
    cout << _name << " " << _color << endl;
}

// 析构函数
Sheep::~Sheep(){
    cout << "调用析构函数" << endl;
}


int main(){
    Sheep sheepA("Doly", "white");
    cout << "sheepA: ";
    sheepA.show();
    Sheep sheepB(sheepA);
    cout << "sheepB: ";
    sheepB.show();
    return 0;
}

输出结果


以张三学习成绩为例写一个拷贝构造函数代码
(1)构造函数体内赋值法
类名::类名(类型名1 参数名1, 类型名2 参数名2){
    成员变量1=参数名1;
    成员变量2=参数名2;
}

Student::Student(string name, double score){
    _name = name;
    _score = score;
}
#include<iostream>
#include<string>
using namespace std;

//含有构造函数、拷贝构造函数、析构函数的类
class Student{
public: 
    Student(string name, double score);
    Student(const Student& other);
    void show();
    ~Student();    
private:
    string _name;    
    double _score;
};

//类外定义构造函数
Student::Student(string name, double score){
    cout << "构造函数" << endl;
    _name = name;
    _score = score;
}

//类外定义拷贝构造函数
Student::Student(const Student& other){
    cout << "拷贝构造函数" << endl;
    _name = other._name;
    _score = other._score;
}


//类外定义成员函数
void Student::show(){
    cout << _name << " " << _score << endl; 
}

//类外定义析构函数
Student::~Student(){
    cout << "析构函数" << endl; 
}

int main(){
    Student stuA("张三", 99.5);
    stuA.show();  
    Student stuB("张三", 99.5);
    stuB.show();

    return 0;
}

 
构造函数
张三 99.5
拷贝构造函数
张三 99.5
析构函数
析构函数

(2)成员初始化列表法

类名::类名(类型名1 参数名1, 类型名2 参数名2): 初始化成员变量{
}

类名::类名(类型名1 参数名1, 类型名2 参数名2): 成员变量1(参数1), 成员变量2(参数2){
}
Student::Student(string name, double score):_name(name), _score(score){
}

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

class Student {
public:
    // 构造函数使用成员初始化列表(尽管在这里不是必需的,因为成员变量是简单类型)
    Student(string name, double score) : _name(name), _score(score) {
        cout << "构造函数" << endl;
    }

    // 拷贝构造函数
    Student(const Student& other) {
        cout << "拷贝构造函数" << endl;
        _name = other._name;
        _score = other._score;
    }

    // show方法不使用const
    void show() {
        cout << _name << " " << _score << endl;
    }

    // 析构函数
    ~Student() {
        cout << "析构函数" << endl;
    }

private:
    string _name;
    double _score;
};

int main() {
    // 创建第一个Student对象
    Student stuA("张三", 99.5);
    stuA.show();

    // 这里实际上调用的是构造函数,不是拷贝构造函数
    // 如果想观察拷贝构造函数的调用,可以创建一个临时对象,然后通过赋值或传递来触发拷贝构造
    // 例如:
    // Student temp("李四", 88.0);
    // Student stuB = temp; // 这里会调用拷贝构造函数
    // 或者通过函数返回对象等方式
    // 但为了保持示例简单,我们再次调用构造函数创建一个新对象
    Student stuB("李四", 88.0);
    stuB.show();

    // 注意:在main函数结束时,stuA和stuB的析构函数会被自动调用
    // 输出顺序可能是stuB的析构,然后是stuA的析构,这取决于编译器的实现和对象的销毁顺序

    return 0;
}

三、浅拷贝

浅拷贝是指在对象复制过程中,仅复制对象的直接成员变量,而不递归复制成员变量所指向的内容
如果成员变量是指针或引用,浅拷贝只是复制指针或引用本身,而不是它们所指向的实际数据

特点:

  1. 只复制一层:只复制对象的直接成员,不涉及成员的成员。

  2. 共享底层数据:如果成员变量是指针或引用,浅拷贝后,多个对象会共享同一块底层数据。

  3. 节省内存:因为不复制底层数据,所以内存占用少,速度也更快。

优点:

  • 性能高,因为不需要复制大量数据。

  • 简单易实现,通常直接赋值即可。

缺点:

  • 如果底层数据被多个对象共享,可能会导致数据一致性问题。

  • 在析构时容易出现双重释放问题,因为多个对象可能尝试释放同一块内存。

基础课笔记:

简单赋值,堆内数值不能拷贝
成员变量是指针或引用,浅拷贝只简单指针赋值,指向旧的地址,并没有为新对象申请新空间。
 

下面代码,在前面代码基础上添加了指针char* _home成员变量
操作:
类外定义(实现)构造函数时,
构造函数内部为_home申请了堆内空间
strcpy将 形参home 数据给了成员变量_home

//为指针成员home分配空间, 将形参home的内容复制到_home指向的空间

   //浅拷贝
    int len = strlen(home) + 1;
    _home = new char[len];
    memset(_home, 0 len);
    strcpy(_home, home); 

#include<iostream>
#include<string>
#include<cstring>

using namespace std;

class Sheep{// 定义羊类
public:
    Sheep(string name, string color, char* home); // 声明有参构造函数
    Sheep(const Sheep& other); //声明拷贝构造函数
    void show(); // 声明成员函数
    ~Sheep();  //声明析构函数
private:
    string _na
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计算机视觉-Archer

图像分割没有团队的同学可加群

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值