首页 > 编程语言 >c++--拷贝构造函数&友元函数

c++--拷贝构造函数&友元函数

时间:2024-11-08 22:48:27浏览次数:3  
标签:友元 函数 -- 对象 内存 拷贝 data 构造函数

目录

1.拷贝构造函数是什么

2.拷贝构造函数的基本格式

2.1 默认拷贝构造函数(浅拷贝)

2.2 深拷贝(Deep Copy)

2.3 浅拷贝(Shallow Copy)

2.3 浅拷贝和深拷贝总结

2.友元函数


1.拷贝构造函数是什么

拷贝构造函数是一个特殊的构造函数,用于在创建新对象时,用已有对象的数据来初始化新对象。拷贝构造函数的典型应用场景包括:

  1. 传值参数:当一个对象通过值传递给函数时,编译器会调用拷贝构造函数来创建函数参数的副本。
  • 为了将 obj1 传递给 passByValue,C++ 会调用 拷贝构造函数 创建一个新的 MyClass 对象。这个新的对象会有自己的 str,内容是 obj1.str
  • passByValue 函数内部,调用 obj.display() 显示副本对象的数据。
  • 注意:obj 是一个副本,函数执行完后,它会被销毁,所做的任何更改都不会影响 obj1
  1. 返回值:当函数返回一个对象时,编译器会用返回的对象来创建一个临时副本,然后将该副本返回给调用者。
  • returnByValue 函数中,创建了一个局部对象 temp,它的 str 初始化为 "临时对象"
  • 然后函数通过 return temp;temp 返回给调用者。
  • 返回时,C++ 会调用 拷贝构造函数 来创建一个新的对象,这个新对象的内容就是 temp 对象的副本。这里的拷贝构造函数会执行字符串的复制操作,确保新对象拥有自己的 str 数据。
  1. 对象初始化:创建新对象并将另一个对象赋值给它时,例如 MyClass obj2 = obj1;
#include <iostream>
#include <cstring>

class MyClass {
    char* str;

public:
    // 普通构造函数
    MyClass(const char* s) {
        str = new char[strlen(s) + 1];
        strcpy(str, s);
        std::cout << "调用普通构造函数" << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass &obj) {
        str = new char[strlen(obj.str) + 1];
        strcpy(str, obj.str);
        std::cout << "调用拷贝构造函数" << std::endl;
    }

    // 显示字符串
    void display() const {
        std::cout << "字符串内容: " << str << std::endl;
    }

    // 析构函数
    ~MyClass() {
        delete[] str;
    }
};

// 示例 1:传值参数
void passByValue(MyClass obj) {
    obj.display();
}

// 示例 2:返回值
MyClass returnByValue() {
    MyClass temp("临时对象");
    return temp;
}

int main() {
    // 示例 3:对象初始化
    MyClass obj1("Hello");  // 调用普通构造函数
    MyClass obj2 = obj1;    // 调用拷贝构造函数 (对象初始化)

    // 示例 1:传值参数
    passByValue(obj1);  // 调用拷贝构造函数 (传值参数)

    // 示例 2:返回值
    MyClass obj3 = returnByValue();  // 调用拷贝构造函数 (返回值)

    obj1.display();
    obj2.display();
    obj3.display();

    return 0;
}

2.拷贝构造函数的基本格式

拷贝构造函数的格式如下:

ClassName(const ClassName &other);

这里的参数 other 是对同类型对象的常量引用

使用引用的原因是避免在传参时调用拷贝构造函数,导致递归调用;而使用常量引用可以避免修改原对象。

如果other 是传值传递的,也就是说,编译器需要创建 other 的副本。而为了创建这个副本,编译器会再次调用拷贝构造函数——这样就会导致递归调用,拷贝构造函数会一遍一遍地自己调用自己,最终导致栈溢出错误

2.1 默认拷贝构造函数(浅拷贝)

如果没有定义拷贝构造函数,编译器会生成一个默认拷贝构造函数,对每个数据成员进行浅拷贝。浅拷贝的方式是逐个复制数据成员,即将源对象的每个成员变量直接赋值给新对象。

浅拷贝对包含指针的类可能不适用。如果类中包含动态分配的资源(例如 new 分配的内存),浅拷贝会导致新旧对象指向相同的内存,可能导致一些问题:

  • 双重释放:如果两个对象指向同一块内存,当一个对象析构时会释放这块内存,而当另一个对象析构时会再次尝试释放这块内存,从而引发错误。
  • 数据混乱:如果一个对象修改了内存的内容,那么另一个对象也会受影响,因为它们共享同一块内存。

为了避免这些问题,通常需要自定义拷贝构造函数

2.2 深拷贝(Deep Copy)

深拷贝会创建一个新内存区域,并复制实际的数据到新内存,这样两个对象互不影响。深拷贝适用于包含指针成员的类。

#include <iostream>
#include <cstring>

class DeepCopyClass {
public:
    char* data;

    // 构造函数
    DeepCopyClass(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }

    // 深拷贝构造函数
    DeepCopyClass(const DeepCopyClass &obj) {
        data = new char[strlen(obj.data) + 1];
        strcpy(data, obj.data); // 复制实际内容
    }

    ~DeepCopyClass() {
        delete[] data;
    }
};

int main() {
    DeepCopyClass obj1("Hello");
    DeepCopyClass obj2 = obj1; // 调用深拷贝构造函数

    // 修改 obj2 的数据
    strcpy(obj2.data, "World");

    std::cout << "obj1 数据: " << obj1.data << std::endl; // 输出 "Hello",因为两者独立
    std::cout << "obj2 数据: " << obj2.data << std::endl;

    return 0;
}

2.3 浅拷贝(Shallow Copy)

浅拷贝仅复制对象的非动态内存内容或指针的地址。这意味着:

  • 浅拷贝会将指针地址复制到新对象中,但不会创建新内存。新对象的指针与原对象指向相同的内存区域。
  • 如果两个对象共享同一内存区域时,一个对象的修改会影响另一个对象。
  • 当程序试图释放内存时,会因为多次释放相同的内存而导致悬挂指针内存泄漏问题。
#include <iostream>
#include <cstring>

class ShallowCopyClass {
public:
    char* data;

    // 构造函数
    ShallowCopyClass(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }

    // 浅拷贝构造函数
    ShallowCopyClass(const ShallowCopyClass &obj) {
        data = obj.data; // 仅复制指针地址(浅拷贝)
    }

    ~ShallowCopyClass() {
        delete[] data;
    }
};

int main() {
    ShallowCopyClass obj1("Hello");
    ShallowCopyClass obj2 = obj1; // 调用浅拷贝构造函数

    // 修改 obj2 的数据
    strcpy(obj2.data, "World");

    std::cout << "obj1 数据: " << obj1.data << std::endl; // 输出 "World",因为两者共享相同内存
    std::cout << "obj2 数据: " << obj2.data << std::endl;

    return 0;
}

2.3 浅拷贝和深拷贝总结

特性浅拷贝深拷贝
内存分配复制指针地址,指向同一内存分配新内存,复制数据
内存管理共用同一内存,容易出现问题各自独立,互不影响
适用场景不涉及动态内存分配的简单对象含指针成员的复杂对象
示例复制地址,仅共享数据复制内容,独立内存

2.友元函数

友元函数(Friend Function)是一种特殊的函数,可以访问类的私有(private)和保护(protected)成员,即使它不是该类的成员函数。通常,在面向对象编程中,类的私有和保护成员只能被该类的成员函数或子类访问,而友元函数则打破了这个限制,允许非成员函数访问类的私有和保护成员。

友元函数的定义方式是在类内部使用关键字 friend 声明该函数

  #include <iostream>
using namespace std;

class Box {
private:
    int width;

public:
    Box() : width(0) {}

    // 将 friend 关键字用于函数声明,使其成为友元函数
    friend void setWidth(Box &box, int w);
};

// 友元函数定义
void setWidth(Box &box, int w) {
    // 可以直接访问 Box 类的私有成员 width
    box.width = w;
}

int main() {
    Box box;
    setWidth(box, 10);  // 调用友元函数
    return 0;
}

setWidthBox 类的友元函数,虽然它不是 Box 类的成员函数,但可以直接访问 Box 类的私有成员 width

通过友元函数,可以实现:访问私有和保护成员:在特定情况下,让非成员函数或其他类可以访问类的私有和保护成员。

标签:友元,函数,--,对象,内存,拷贝,data,构造函数
From: https://blog.csdn.net/qq_63152518/article/details/143538251

相关文章

  • c语言--数组
    目录1数组创建 1.1定义定长数组1.2定义并初始化数组1.3定义部分初始化的数组2.动态数组(动态分配)2.1使用malloc动态创建数组2.2使用calloc动态创建数组2.3动态数组初始化2.4释放动态数组内存3.变长数组(VLA,VariableLengthArray)4.字符串和字符数组4.1.......
  • 每日OJ题_牛客_BC157素数回文_数学_C++_Java
    目录牛客_BC157素数回文_数学题目解析C++代码Java代码牛客_BC157素数回文_数学素数回文_牛客题霸_牛客网描述:现在给出一个素数,这个素数满足两点:1、  只由1-9组成,并且每个数只出现一次,如13,23,1289。2、  位数从高到低为递减或递增,如2459,87631。请你判断一下,这......
  • Ensp命令
    华为eNSP指令完全帮助shift+?sysname(修改名字)tracert(查看路由路径)system-view(进入系统视图)interfaceEthernet0/0/0(进入接口视图)ipaddressIP地址子网掩码(配置接口地址)displaythis(查看当前配置)iproute-static[目的网段][子网掩码][下一条地址](设置路由表)i......
  • 三层交换机及ensp配置详细教学
    三层交换机?三层交换机就是具有部分路由器功能的交换机,工作在OSI网络标准模型的第三层:网络层。三层交换机的最重要目的是加快大型局域网内部的数据交换,所具有的路由功能也是为这目的服务的,能够做到一次路由,多次转发。2实验拓扑2.1新建拓扑如下图 2.2pc机ip配置 Pc1pc2......
  • Ensp静态路由配置
    Ensp静态配置详解一、基本概念静态路由 是一种通过手动配置路由表来指导数据包转发的方式。在网络中,管理员需要手动添加路由条目,指定目标网络的下一跳地址。静态路由具有配置简单、资源消耗少、安全性高等优点,但也存在无法动态适应网络变化、维护困难等问题。 eNSP 是华......
  • JavaWeb --cookie和session技术
    Cookiecookie*客户端技术,存放在客户端,由服务器发放给用户保存*一个Cookie只能存放一个键值对Cookiecookie=newCookie(""(name),""(value));//设置cookie有效期单位秒//>0表示cookie有效期//=0删除cookie//<0会话cookiecookie.setMaxAge(0);//将cookie响应给客......
  • 【高等数学】微分学的应用
    中值定理罗尔中值定理fff在[a,......
  • Stack模块的设置
    TEAM:TopologicalEvolution-awareFrameworkforTrafficForecasting–ExtendedVersionMotivation为了捕捉复杂的时空动态,许多基于深度学习的方法最近被提出,并由于其学习非线性动力学[35,59]的能力,在挑战数据集上显示出了有希望的结果。这些方法通常建立在图神经网络(GNNs)[16......
  • huawei初级网络工程师综合实验
    本章为总结练习,只提供思路以及验证结果,和比较有难度的命令并且在我的其他章节对本练习中出现的所有都有介绍这里就不重复解释了拓扑图以及实验要求:sw1充当2层交换机sw-2(undoportswitch)充当三册交换机R-3连接外网1.基本ip配置略2.sw-2和R-2间开启rip协议R-2R-3......
  • 讲座の题解
    讲座配套题单的题解喵每题的文字解释会逐渐补充,如果有疑问直接私信喵目录讲座配套题单的题解喵目录A-看看你会不会用电脑B-求求你不要用内置函数C-GPAD-minE-for循环大神F-居然有人说这个是线性代数G-高三同学秒了H-无穷级数I-不要用内置函数......