深浅拷贝 & 类型萃取

一、深浅拷贝问题

             1)什么是浅拷贝
所谓的浅拷贝是指在源对象向目标对象进行拷贝的时候 将源对象的各个成员逐一赋给新对象的成员
这样做看似无可厚非,实际上如果源对象的数据成员中有指向一片内存空间的指针变量是 这种浅拷贝只是将这个指针变量作为右值付给目的对象的指针。

这样做的结果是新对象的指针和源对象的指针指向同一片内存空间 。
改动数据时在用户的角度上看似改动了拷贝后的目的对象,实际上也将源对象的数据改动了。
主程序结束时,对象被逐个撤销,先撤销对象st2(因为st2后创建),撤销前先调用析构函数,用delete运算符释放所分配的内存空间;撤销对象st1时,第二次调用析构函数,因为这时_name所指向的内存空间已经被释放,企图对同一块内存空间释放两此,所以这时候程序出错。
              如图所示:
            
                                   
注意:C++中类的默认构造函数 和库中一些实现数据拷贝的库函数都是浅拷贝的

2)所谓深拷贝就是为新的对象指针开辟一段新空间 在这段空间内赋源对象指针空间内的数据内容 这样目的对象和源对象有两个类容一样的空间。
更改和释放时都对自己管理的空间进行操作 
如果要解决浅拷贝问题就必须实现深拷贝
1.类中自定义的拷贝构造函数和赋值运算符的重载
   两种写法:
基本写法:
基本写法的思路是 为目的对象的成员指针开辟合适的空间  之后将源对象的指针赋给新对象将源对象指针指向空间的数据内容赋给新对象指针空间
          String(constString& s)
          {
              _str =newchar[strlen(s._str) + 1];
              strcpy(_str, s._str);
          }
          String&operator=(constString& s)
          {
              if(this!= &s)
              {
                   char* tmp = new char[strlen(s._str) + 1];
                   strcpy(tmp, s._str);
                   delete[] _str;
                   _str = tmp;
              }
              return*this;//为了支持链式访问
          }
如图:
图中红色箭头代表拷贝
现代写法:
现代写法的基本思路是 利用写好的构造函数在烤贝构造函数和赋值运算符重载内部构造出一个临时对象
之后将目的对象的成员变量与临时对象的成员变量交换 这是目的对象中指针的指向是临时对象中指针指向的
用构造函数创建的空间 该空间中数据内容和源对象一样是两份。
拷贝构造函数结束后临时对象调用析构函数释放了原目的对象的对象空间,原本的临时对象作为目的对象被保留了下来.


代码:
          String(char* str = "")
              :_str(newchar[strlen(str) + 1])
          {
              strcpy(_str, str);
          }
          String(constString& s)
              :_str(NULL)
          {
              Stringtmp(s._str);
              swap(_str, tmp._str);
          }
          String&operator= (constString& s)
          {
              if(this!= &s){
                   Stringtmp(s._str);
                   swap(_str, tmp._str);
              }
              return*this;
          }

什么是类型萃取:
现在先定义一个函数:
//template<class T>
//void Copy(T* dst, T*src,size_t size)
//{
//   memcpy(dst, src, sizeof(T)*size);
//}
该函数调用memcpy()该函数将源地址中的数据按规定的字节以二进制形式拷贝到新空间去
首先在进行拷贝的是Sring类的对象数组,而memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
此时s1,s2指向的是同一块内存,并且s2原有的内存丢失了,析构的时候先将s2析构掉,再将s1析构掉,此时同一块内存会被析构多次,则程序一定会崩溃。
此时我们不能选择用memcpy进行拷贝,选择使用for循环

内置类型我们可以通过memcopy和memmove这两个方式进行拷贝,自定义类型或string我们要通过赋值的方式拷贝。这样的话会提高效率。

类型萃取的方式:类型萃取是在模板的基础上区分内置类型和其他类型,主要原理是将内置类型全部特化,然后再进行区分。


#include<iostream>
#include<stdlib.h>
#include<string>
usingnamespacestd;
//
//template<class T>
//void Copy(T* dst, T* src,size_t size)
//{
//   memcpy(dst, src, sizeof(T)*size);
//}
struct_TrueType
{
     staticboolGet()
     {
          returntrue;
     }
};
struct_FalseType
{
     staticboolGet()
     {
          returnfalse;
     }
};
template<classT>
structTypeTraits
{
     typedef_FalseType_IsPod;
};
template<>
structTypeTraits<int>
{
     typedef_TrueType_IsPod;
};
template<>
structTypeTraits<char>
{
     typedef_TrueType_IsPod;
};
template<classT>
voidCopy(T* dst, T* src,size_tsize)
{
     if(TypeTraits<T>::_IsPod().Get())//构建一个临时对象
          memcpy(dst, src,sizeof(T)*size);
     else{
          for(size_ti = 0; i < size; ++i){
              dst[i] = src[i];
          }
     }
}
intmain()
{
     
     stringa[4] = { "111","222","333"};
     stringb[4] = { "444","555","666"};
   intdata = 9;
     int_data = 8;
     //Copy(a, b, 4);
     Copy<string> (a, b, sizeof(a) /sizeof(a[0]));
     Copy<int>(&data, &_data, 1);
     system("pause");
     return0;
}


### 类型萃取的概念与实现方式 类型萃取(Type Traits)是一种在C++模板编程中常见的技术,用于提取或操作类型信息。它能够帮助程序员在编译期获取类型的属性或特性,从而实现更灵活的泛型编程。 #### 1. 类型萃取的基本概念 类型萃取可以简单理解为从某个类型中提取出相关信息[^2]。这种技术通常用于模板编程中,特别是在需要区分模板参数是基本类型还是复杂类型时非常有用。例如,在模板函数中,可能需要判断类型是否为POD(Plain Old Data)类型,或者是否支持某些特定的操作。 #### 2. 类型萃取的应用场景 类型萃取的一个典型应用场景是在模板特化中。通过类型萃取,可以为不同的类型提供不同的实现。例如,对于POD类型和非POD类型,可能需要不同的处理逻辑。以下是类型萃取的一些常见应用: - 判断类型是否为引用、指针或常量。 - 判断类型是否具有某些特性,如是否可复制、是否可移动等。 - 提取容器的元素类型、迭代器类型等。 #### 3. 类型萃取的代码实现 以下是一个简单的类型萃取器的实现示例,展示了如何通过模板特化来实现类型萃取。 ```cpp // 基础模板 template &lt;typename T&gt; struct TypeTraits { typedef T value_type; // 萃取出T的类型 }; // 特化模板:针对指针类型 template &lt;typename T&gt; struct TypeTraits&lt;T*&gt; { typedef T value_type; // 萃取出指针指向的类型 }; // 测试代码 #include &lt;iostream&gt; int main() { TypeTraits&lt;int&gt;::value_type x = 42; TypeTraits&lt;int*&gt;::value_type y = 0; std::cout &lt;&lt; &quot;x: &quot; &lt;&lt; x &lt;&lt; std::endl; // 输出42 std::cout &lt;&lt; &quot;y: &quot; &lt;&lt; y &lt;&lt; std::endl; // 输出0 return 0; } ``` 上述代码中,`TypeTraits` 是一个通用的类型萃取器,用于提取类型的信息。通过模板特化,可以为指针类型提供特殊的处理逻辑[^3]。 #### 4. 模板特化与类型萃取的关系 模板特化是实现类型萃取的核心机制之一。通过模板特化,可以根据不同的类型提供不同的实现。例如,对于指针类型和非指针类型,可以通过特化模板分别定义不同的行为。 以下是一个使用模板特化的例子,展示如何根据类型的不同提供不同的实现: ```cpp // 基础模板 template &lt;typename T&gt; struct IsPointer { static const bool value = false; // 默认不是指针类型 }; // 特化模板:针对指针类型 template &lt;typename T&gt; struct IsPointer&lt;T*&gt; { static const bool value = true; // 指针类型 }; // 测试代码 #include &lt;iostream&gt; int main() { std::cout &lt;&lt; IsPointer&lt;int&gt;::value &lt;&lt; std::endl; // 输出0(不是指针) std::cout &lt;&lt; IsPointer&lt;int*&gt;::value &lt;&lt; std::endl; // 输出1(是指针) return 0; } ``` 在上述代码中,`IsPointer` 是一个布尔类型的特质类,用于判断给定类型是否为指针类型。通过模板特化,可以为指针类型提供特定的实现。 #### 5. 标准库中的类型萃取 C++标准库提供了丰富的类型萃取工具,位于 `&lt;type_traits&gt;` 头文件中。这些工具可以帮助开发者更方便地进行类型操作。例如: - `std::is_pointer`:判断类型是否为指针。 - `std::is_const`:判断类型是否为常量。 - `std::remove_reference`:去除类型的引用部分。 - `std::conditional`:根据条件选择不同的类型。 以下是一个使用标准库中类型萃取的例子: ```cpp #include &lt;iostream&gt; #include &lt;type_traits&gt; int main() { std::cout &lt;&lt; std::is_pointer&lt;int&gt;::value &lt;&lt; std::endl; // 输出0 std::cout &lt;&lt; std::is_pointer&lt;int*&gt;::value &lt;&lt; std::endl; // 输出1 std::cout &lt;&lt; std::is_const&lt;const int&gt;::value &lt;&lt; std::endl; // 输出1 std::cout &lt;&lt; std::is_const&lt;int&gt;::value &lt;&lt; std::endl; // 输出0 return 0; } ``` ### 总结 类型萃取是C++模板编程中的一项重要技术,能够帮助开发者在编译期获取类型的属性或特性。通过模板特化,可以为不同的类型提供不同的实现,从而实现更灵活的泛型编程。C++标准库中的 `&lt;type_traits&gt;` 提供了丰富的类型萃取工具,极大地简化了类型操作的复杂性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值