从String类中看C++当中的深拷贝与浅拷贝解

了解过C++语言的人,都应该知道,C++语言中的构造函数,析构函数,拷贝构造函数,赋值运算符重载函数,如果不定义,编译器会自动生成的,当然,生成的只是一些最基本的,在达不到我们要求的条件下,就需要我们自己重新定义这些函数。

我们现在说的讲的是深拷贝与浅拷贝,当然讨论这个问题的基础,一般情况下是我们定义的变量是以指针形式出现的,原因就在于,不论赋值还是拷贝,我们要实现两个指针指向的内容一致,那我得到新的指针和原指针是指向同一块内存空间还是两块内存空间的相同内容。如果是指向同一块空间,就存在了安全隐患,我们在自己管理空间时,就很有可能对一块空间进行了多次的释放,有些编译器下会报错,甚至直接奔溃,有些编译器虽不会抛出异常,但也会对后续的程序造成未知的错误。

那么,现在就以String为例,看看如何解决这些问题。

//参数列表中,pstr为char* 类型,s为String类型

//实现方法1:
class String
{
 friend ostream& operator<<(ostream& _out, const String& s);
public:
 String(const char* pstr) :_str(new char[strlen(pstr)+1])
 {
  strcpy(_str,pstr);
  cout << "构造" << endl;//做标识
 }
 String(const String& s) :_str(new char[strlen(s._str) + 1])
 {
  strcpy(_str,s._str);
  cout << "拷贝构造"<<endl;//做标识
 }
 String& operator = (const String& s)
 {
  if (this == &s)//不给自己赋值,防止对一块空间进行二次析构
  {
   char* tmp = (new char[strlen(s._str) + 1]);
   strcpy(tmp, s._str);
   delete[]_str;
   _str = tmp;
  }
  cout << "赋值重载" << endl;//做标识
  return *this;  //注意返回值
 }
 ~String()
 {
  if (this != NULL)
  {
   delete[]_str;
   _str = NULL;
  }
  cout << "析构" <<endl;//做标识
 }
private:
 char *_str;
};

//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
 _out << s._str<< endl;
 return _out;
}

//*****************************************************************************************

//String类的实现2

class String
{
 friend ostream& operator<<(ostream& _out, const String& s);
public:
 String(const char* str) :_str(new char[strlen(str)+1])//同上
 {
  strcpy(_str,str);
 }

//拷贝构造
 String(const String& s) : _str(NULL)
 {
  String tmp(s._str);
  std::swap(_str,tmp._str);
 }

//赋值运算符重载
 //String&operator=(const String& s)
 //{
 // if (this != &s)
 // {
 //  String tmp(s._str);//用s._str或者s实例化tmp都可以
 //  std::swap(_str,tmp._str);
 // }
 // return *this;
 //}
 
 //赋值运算符重载之开辟空间法
 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()
 {
  if (NULL == _str)
  {
   delete[] _str;
   _str = NULL;
  }
 }
private:
 char* _str;
};

//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
 _out << s._str<< endl;
 return _out;
}

//*****************************************************************************************

// String类的实现3------>浅拷贝实现防止内存的多次释放

class String
{
 friend ostream& operator<<(ostream& _out, const String& s);
public:
 String(const char* pdata)
  :_str(new char[strlen(pdata)+1])
  , _count(new int)
 {
  strcpy(_str,pdata);
  *_count = 1;
 }
 String(const String& s)
  :_str(s._str)
  , _count(s._count)
 {
  _count++;
 }
 
 String& operator=(const String& s)
 {
  if (this != &s)
  {
   if (--(*_count) == 0)
   {
    delete[]_str;
    delete _count;
   }
   _count = s._count;
   _str = s._str;
   _count++;
  }
  return* this;
 }

~String()
 {
  if (--(*_count) == 0)
  {
   delete[] _str;
   delete _count;
  }
 }
private:
 char* _str;
 int* _count;
};

//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
 _out << s._str<< endl;
 return _out;
}

//*****************************************************************************************

友元函数受第一参数的限制,只能采用类外定义,引入友元

方法三,关于引入计数器,在面试过程中,这是最不推荐的方法,HR往往更倾向于让你写出深拷贝的方法。

深拷贝与浅拷贝是面试过程中经常出现的题目,弄清楚这类问题是非常必要的

时间: 2024-10-10 15:55:58

从String类中看C++当中的深拷贝与浅拷贝解的相关文章

C++【常见面试题】String类的实现,以及深拷贝、浅拷贝问题

浅拷贝:实现不了,由于两个指针指向同一块空间,则空间释放时会释放两次,导致程序奔溃.这也是深拷贝与之不同要改进的地方.  深拷贝:重新开辟空间,将原来的空间拷贝过来,再把值复制过来.(采用String的现代写法) 深拷贝: #include<iostream> #include<stdlib.h> using namespace std; class String { public:     String(const char* str)         :_str(new cha

C++ Primer 学习笔记_20_类与数据抽象(6)_深拷贝与浅拷贝、空类与空数组

C++ Primer 学习笔记_20_类与数据抽象(6)_深拷贝与浅拷贝.空类与空数组 一.深拷贝与浅拷贝 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象. 深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量.那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换而言之,深拷贝把要复制的对象所引用的对象都复制了一遍. 浅拷贝可

【C语言】【面试题】C++中String类浅拷贝,深拷贝的传统写法与现代写法

C++ 的一个常见面试题是让你实现一个 String 类,在这我把String类的各种写法写了一下 1.浅拷贝 #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; //1.浅拷贝 class String { public:     String(char* str)         :_str(str)     {}     String(const String& s)         

【C语言】【面试题】C++中String类引用计数器的浅拷贝写法与深拷贝写法

Linux操作下String类的实现--引用计数器 1.引用计数器写法一 写法一个人比较喜欢叫他双指针法,因为他是在类里面创建了两个指针来实现的一个是指针_str,另外一个是用来保存指向同一块空间个数的指针_pRefCount. class String { public:     String(char* str = "")         :_str(new char[strlen(str) + 1])         , _pRefCount(new int(1))     {

STL库中string类的探究

在STL中有着一个类就是string类,他的内存布局和存储机制究竟是怎么样的呢? 这就是建立好的string 可以看出,图中用黄色框框标注的部分就是主要区域 我们用来给string对象进行初始化的字符串被存储在了_Buf当中,_Mysize和_Myres就不用说了,就是上面的size 和 capacity 的值. 当只有一个字符作为字符串的时候,就可以很明显的看出来了,_Mysize是指字符串的length _Myres还是没有改变,_Myres最大就是15么? 此时还没有变化,再加入一个字符

C++ 类、构造析构、深拷贝

1st,感性理解类的思想,就是把数据和对数据的操作打包在一块儿,设计类的时候要 想好数据部分和 要进行的操作.以下是时间类的示意,时间包含时分秒,Time为构造函数,该类支持的操作就是设置时间和读取时间,static类型记录对象数量,static机制查询相关书籍. //Time.h #ifndef Time_h #define Time_h class Time { public: Time(int h = 0, int m = 0, int s = 0); void SetTime(int,

String类的实现与深浅拷贝问题

C++实现string类是理解类和对象这个概念的基础,也能了解C++语法的特性--用户对内存的自主管理,通过类创建出一个对象的过程,首先要调用构造函数,经过一系列的操作,当退出对象所在的作用域时,便会调用析构函数,C++支持隐式的调用构造.析构等函数,但经常隐式调用并不能正确的管理内存,所以我们需要重写这两个函数,以及拷贝构造,和赋值运算符的重载. string类的结构设计 string类型的设计源于我们队字符串操作及封装的需求,所以string类是对C语言中的字符串进行优化故设计代码如下: c

20140526-一个从pdf转换成图片的类,工作当中有用到

20140526-一个从pdf转换成图片的类,工作当中有用到 package com.jako.database.model; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import

C++ string类

string类是C++的一个常见的面试题,考查面试者的基本功,虽然简单,但是有很多细节需要注意. #pragma once #include <assert.h> /* 深拷贝与浅拷贝:         浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝, 而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针.浅拷贝是指源对象 与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同).对其中任何一个对象的改动都会影响另外一个对象.     深