深浅拷贝 & 类型萃取

一、深浅拷贝问题

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

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

2)所谓深拷贝就是为新的对象指针开辟一段新空间 在这段空间内赋源对象指针空间内的数据内容 这样目的对象和源对象有两个类容一样的空间。
更改和释放时都对自己管理的空间进行操作 
如果要解决浅拷贝问题就必须实现深拷贝
1.类中自定义的拷贝构造函数和赋值运算符的重载
   两种写法:
基本写法:
基本写法的思路是 为目的对象的成员指针开辟合适的空间  之后将源对象的指针赋给新对象将源对象指针指向空间的数据内容赋给新对象指针空间
           String ( const String & s)
          {
              _str = new char [ strlen (s._str) + 1];
               strcpy (_str, s._str);
          }
           String & operator= ( const String & 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( new char [ strlen (str) + 1])
          {
               strcpy (_str, str);
          }
           String ( const String & s)
              :_str( NULL )
          {
               String tmp(s._str);
               swap (_str, tmp._str);
          }
           String & operator = ( const String & s)
          {
               if ( this != &s){
                    String tmp(s._str);
                    swap (_str, tmp._str);
              }
               return * this ;
          }

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

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

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


#i nclude <iostream>
#include <stdlib.h>
#include <string>
using namespace std;
//
//template<class T>
//void Copy(T* dst, T* src,size_t size)
//{
//   memcpy(dst, src, sizeof(T)*size);
//}
struct _TrueType
{
      static bool Get()
     {
           return true ;
     }
};
struct _FalseType
{
      static bool Get()
     {
           return false ;
     }
};
template < class T >
struct TypeTraits
{
      typedef _FalseType _IsPod ;
};
template <>
struct TypeTraits < int >
{
      typedef _TrueType _IsPod ;
};
template <>
struct TypeTraits < char >
{
      typedef _TrueType _IsPod ;
};
template < class T >
void Copy ( T * dst, T * src, size_t size)
{
      if ( TypeTraits < T >::_IsPod().Get()) //构建一个临时对象
          memcpy(dst, src, sizeof ( T )*size);
      else {
           for ( size_t i = 0; i < size; ++i){
              dst[i] = src[i];
          }
     }
}
int main ()
{
     
      string a[4] = { "111" , "222" , "333" };
      string b[4] = { "444" , "555" , "666" };
    int data = 9;
      int _data = 8;
      //Copy(a, b, 4);
      Copy < string > (a, b, sizeof (a) / sizeof (a[0]));
      Copy < int >(&data, &_data, 1);
      system ( "pause" );
      return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值