怎么样在百度做网站,网站优化就是seo,wordpress入站密码,建设摩托车官网报价一 拷贝构造函数的概念#xff1a;
拷贝构造函数是一种特殊的构造函数#xff0c;用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时#xff0c;或者将对象传递给函数或从函数返回对象时#xff0c;会调用拷贝构造函数。
二 拷贝构造函…一 拷贝构造函数的概念
拷贝构造函数是一种特殊的构造函数用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时或者将对象传递给函数或从函数返回对象时会调用拷贝构造函数。
二 拷贝构造函数的特点
1拷贝构造函数是构造函数的一个重载形式。
2拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错 因为会引发无穷递归调用。
3若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。
注意在编译器生成的默认拷贝构造函数中内置类型是按照字节方式直接拷贝的而自定 义类型是调用其拷贝构造函数完成拷贝的。
4编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了还需要自己显式实现吗 当然像日期类这样的类是没必要的。
2.1 代码示例
class Time
{
public:// 普通构造函数Time(int hour 0, int minute 0, int second 0) {_hour hour;_minute minute;_second second;}// 拷贝构造函数使用引用传递Time(const Time other) {_hour other._hour;_minute other._minute;_second other._second;}void Print() const {std::cout _hour : _minute : _second std::endl;}private:int _hour;int _minute;int _second;
};int main()
{Time t1(10, 20, 30); // 使用普通构造函数//构造函数的重载Time t2 t1; // 使用拷贝构造函数//Time t2(t1); // 拷贝构造的另一种写法t1.Print();t2.Print();return 0;
}
输出 2.2 为什么要使用引用呢
我们在 increment 函数中改变x的值并没有间接性改变a这是因为传过去的只是编译器创建实参的一个副本而修改副本怎么可能可以改变a呢
#include iostreamvoid increment(int x)
{x x 1; // 修改的是副本不影响实参
}int main()
{int a 5;increment(a); // 传递a的副本std::cout a std::endl; // 输出5原始值a未被修改return 0;
}知道传值传参的本质之后再来想一想为什么要用引用咱们先来说说如果没用用引用的后果会是怎么样当把自定义类型传出去后且不用引用或者指针来接收它会
调用 Time(const Time other)其中 other 是 t1 的按值传递副本。
为了按值传递编译器需要创建 other 的副本。
创建 other 的副本时再次调用 Time(const Time other)。
这个新调用的 Time(const Time other) 又需要创建自己的 other 副本再次调用 Time(const Time other)。
如此反复导致无限递归调用最终导致栈溢出。
图 C规定自定义类型的拷贝都会调用拷贝构造 那为什么要引用呢
首先我们来回顾一下引用
1引用是现有变量的另一个名字。
2它们不创建新对象只是指向已有对象。
3引用只是指向现有对象不创建新副本
因为引用就是它本身所以何来创建新副本这一说法创建新副本是怕改变副本从而导致改变实参值
2.3 总结
1按值传递会递归每次传递对象会复制对象导致无限递归。
2引用传递避免递归引用只是指向对象本身不会复制对象
三 默认拷贝构造
当你没有显式定义拷贝构造函数时编译器会为你自动生成一个默认的拷贝构造函数。这个默认拷贝构造函数会逐个拷贝对象的所有成员变量。
3.1 内置类型与自定义类型的拷贝
内置类型如 int, char, float 等拷贝时直接按照字节方式进行复制也就是直接复制其值。
自定义类型如类和结构体拷贝时会调用该类型的拷贝构造函数。
3.2 代码示例
内置类型
#include iostreamclass MyClass
{
public:int x; // 内置类型成员
};int main()
{MyClass obj1;obj1.x 10;MyClass obj2 obj1; // 使用编译器生成的默认拷贝构造函数std::cout obj1.x: obj1.x std::endl; std::cout obj2.x: obj2.x std::endl;return 0;
}输出 对于一个类里面只有内置类型成员那编译器生成的默认拷贝构造会自动复制其值。
自定义类型
#include iostreamclass Time
{
public:// 默认构造函数Time() { _hour 0;_minute 0;_second 0;}// 拷贝构造函数Time(const Time other) {_hour other._hour;_minute other._minute;_second other._second;std::cout Time::Time(const Time other) std::endl;}private:int _hour;int _minute;int _second;
};class MyClass
{
public:int x; // 内置类型成员Time t; // 自定义类型成员
};int main()
{MyClass obj1;obj1.x 10;MyClass obj2 obj1; // 使用编译器生成的默认拷贝构造函数std::cout obj1.x: obj1.x std::endl;std::cout obj2.x: obj2.x std::endl; return 0;
}
当执行MyClass obj2 obj1; 因obj1类里面有自定义类型 t 所以编译器生成的默认拷贝构造会自动调用Time(const Time other) 来完成
3.3 总结
内置类型编译器默认拷贝构造函数会直接复制其值。
自定义类型编译器默认拷贝构造函数会调用该类型的拷贝构造函数来复制其内容。
四 内存分区
要理解好深拷贝与浅拷贝那就得先了解内存是怎么样分区的。 计算机程序运行时内存通常被分为四个主要区域栈区、堆区、全局静态区和只读区常量区和代码区。
4.1 栈区
局部变量函数内部定义的变量。
形参函数参数函数定义时的参数。
返回地址函数调用后的返回地址。
特点
栈区中访问速度快且栈的内存连续分配。
因存储的都是 局部/形参/返回地址 所以栈区空间小存储的生命周期短。
在我们局部变量所在的函数执行完成时它会自动释放内存。
4.2 堆区
动态分配的数据通过 new 或 malloc 等动态分配函数分配的内存。
特点
因存储的都是new 或者malloc开辟的空间所以堆区空间大所以访问速度慢。
堆中的内存分配和释放是通过指针进行的可能不是连续的。
堆区的内存需要程序员手动管理必须手动释放动态分配的内存否则会导致内存泄漏。
4.3 全区/静态区
全局变量在所有函数外部定义的变量。
静态变量使用 static 关键字定义的变量。
特点
全局变量和静态变量在程序的整个运行期间一直存在直到程序结束。
全局变量可以在程序的所有函数中访问静态变量在声明的作用域内共享。
4.4 只读常量区
常量程序中定义的常量。
代码程序的指令代码。
特点
常量区的数据在程序运行期间不能被修改保证了数据的安全性和稳定性。
代码区存储程序的指令代码在程序运行时被载入内存以执行。
五 浅拷贝
首先我们来回顾C语言里面的基本类型和指针类型。
5.1 基本类型
基本类型是C语言内置的数据类型它们用于存储最基本的数值数据。常见的基本类型包括int float char……
5.2 指针类型
指针类型是存储内存地址的数据类型。指针用于指向其他变量或对象在内存中的位置。
5.3 基本类型代码示例
#include iostreamclass BasicType
{
public:int value;// 构造函数BasicType(int v) {value v;}// 拷贝构造函数BasicType(const BasicType other) {value other.value;}
};int main()
{BasicType obj1(10);BasicType obj2 obj1; // 浅拷贝复制基本类型的值std::cout 改变前: std::endl;std::cout obj1.value: obj1.value std::endl;std::cout obj2.value: obj2.value std::endl;obj2.value 20; // 修改obj2的值std::cout 改变后: std::endl;std::cout obj1.value: obj1.value std::endl;std::cout obj2.value: obj2.value std::endl;return 0;
}输出 值会被复制但修改新对象的值不会影响原对象。 5.3 指针类型代码示例
#include iostreamclass SimplePointer
{
public:int* ptr; // 成员变量 ptr// 构造函数SimplePointer(int value)
{ptr (int*)malloc(sizeof(int)); // 动态分配内存并初始化if (ptr ! nullptr) {*ptr value;}
}SimplePointer(const SimplePointer other) {this-ptr other.ptr; // 浅拷贝复制内存地址}void print() const {std::cout Value: *ptr std::endl;}
};int main()
{SimplePointer obj1(10); // 创建第一个对象并将值初始化为10SimplePointer obj2(obj1); // 使用拷贝构造函数浅拷贝// 打印初始值std::cout Initial values: std::endl;obj1.print();obj2.print();// 修改obj2的值*obj2.ptr 20;// 打印修改后的值std::cout After change: std::endl;obj1.print();obj2.print(); return 0;
}
输出 复制内存地址共享同一块内存修改会互相影响 六 深拷贝
#include iostream
#include cstdlib
#include cstringclass SimpleClass
{
public:int* ptr;// 默认构造函数SimpleClass(int value) {ptr (int*)malloc(sizeof(int)); // 动态分配内存并初始化if (ptr ! nullptr) {*ptr value;}}// 深拷贝构造函数SimpleClass(const SimpleClass other) {ptr (int*)malloc(sizeof(int)); // 分配新内存if (ptr ! nullptr) {*ptr *(other.ptr); // 复制内容}}// 析构函数~SimpleClass() {if (ptr ! nullptr) {free(ptr); // 释放内存}}void Print() const {if (ptr ! nullptr) {std::cout Value: *ptr std::endl;}}
};int main()
{SimpleClass obj1(10); // 创建对象ptr 指向的值为 10SimpleClass obj2 obj1; // 使用深拷贝构造函数obj1.Print();obj2.Print();// 修改 obj2 的值if (obj2.ptr ! nullptr) {*(obj2.ptr) 20;}obj1.Print();obj2.Print();return 0;
}输出 深拷贝不仅复制对象的指针成员还为指针指向的内容分配新的内存并复制原对象的数据。这样两个对象拥有独立的内存修改一个不会影响另一个。