您的位置:首页 > 编程语言 > C语言/C++

c++OOP之复制控制 ------复制构造函数、赋值重载、析构

2014-09-26 02:01 204 查看
本博文我们讨论OOP复制控制的一些内容;
首先考虑对象复制的时机: 非引用类型

1):根据一个类去显式或者隐式初始化一个对象;

2):复制一个对象,将它作为实参传给一个函数;

3):从函数返回时复制一个对象。( string tolittle(string word))

4):初始化顺序容器中的元素。(例如vector 必须具备 copy,assignment功能)

一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。(面试)!!


11.复制构造函数、赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个。以String为例:
a)  涉及到深拷贝、浅拷贝问题,所以需要提供拷贝构造函数
b)  然后,为了保持一致,赋值运算符也应该实现深拷贝
c)  既然实现深拷贝,那么必定申请了资源(例如内存),所以必然需要析构函数来手工释放。


一:复制构造函数:

复制构造函数调用的时机就是在对象复制的时候。方式有两种:

1):当用户不提供时,编译器自动为我们合成一个拷贝构造函数;

示例代码如下:

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
Student()
{}
Student(int id, const string&name, int age)
:id_(id),name_(name),age_(age)
{}

void print()
{  cout << id_ <<":" << name_ <<":" << age_ << endl; }
private:
int id_;
string name_;
int age_;
};

int main(int argc, const char *argv[])
{
Student s(11,"zhangsan",23);
s.print();

Student s2(s); //执行此句,调用默认复制构造函数
s2.print();
return 0;
}


2):用户自己定义:

放入class Student 的public中即可;

Student(const Student &s)
{
id_ = s.id_;
name_ = s.name_;
age_ = s.age_;
}


复制构造函数之 深copy 和 浅copy;

含有指针成员变量的类在复制时,有两种选择:
a) 复制指针的值,这样复制完毕后,两个对象指向同一块资源,这叫做 浅拷贝shallow copy(有可能出错)
b) 复制指针所指向的资源,复制完毕后,两个对象各自拥有自己的资源,这叫做 深拷贝 deep copy


示例代码:

#include "iostream"
#include "string.h"
using namespace std;

//copy String 时,仅仅复制指针的值
//导致析构时发生了问题
class String
{
public:
String();
String(const char*s);
String(const String &s);
~String();
void print()const
{
cout << str_ << endl;
}
private:
char *str_;
};
String::String()
:str_(new char(0))
{  }
String::String(const char *s)
:str_(new char[strlen(s)] +1)
{
::strcpy(str_, s);
}
//程序结束时会调用析构函数,两个对象s1,s2必然会析构两次,而str_指向同一片区域;
//当析构s1时,s1.str_所指向的区域被释放;而s2.str_ 也同时被释放
//当析构s2时,由于s2.str_已经被s1.str_释放,所以会产生错误.
String::String(const String &s)
:str_(s.str_)
{}

/* 修改成以下即可:
*String::String(const String &s)
*     :str_(new char[strlen(s)] +1)
* {
*  ::strcpy(str_, s.str_);
*  }
*/
String::~String()
{
delete[] str_; //注意
}
int main(int argc, const char *argv[])
{
String s("helloworld");
s.print();

String s2(s);
s.print();
return 0;
}


二: 赋值运算符的重载:【因为我们自定义的类没有内置比较运算符(<、>、!=)】
示例代码如下:

#include <iostream>
#include <string.h>
using namespace std;

class String
{
public:
String();
String(const char *s);
String(const String &s);
~String();
String &operator = (const String &s);
size_t size()const //自己构造size函数
{
return strlen(str_);
}

void print()
{ cout << str_ << endl; }
private:
char *str_;
};
String::String()
:str_(new char[1])
{ *str_ = 0; }

String::String(const char*s)
:str_(new char(strlen(s)+1))
{ ::strcpy(str_, s); }

String::String(const String &s)
:str_(new char(strlen(s.str_)+1))
{ ::strcpy(str_, s.str_); }

String &String::operator = (const String &s)
{
if(this == &s)//自身赋值
return *this;
delete[]str_;
str_ = new char(s.size() + 1 );
::strcpy(str_, s.str_);

return *this;
}

String::~String()
{ delete[] str_; }

int main(int argc, const char *argv[])
{
String s("helle");
s.print();

String s2;
s2 = s; //赋值重载
s2.print();

s2 = s2 ; //注意自身赋值情形
s2.print();

return 0;
}


禁止类复制和赋值的方法:

a):把 copy构造函数和复制运算符设为private;

b):只声明,不实现;

#include <iostream>
#include <string>
#include <vector>
using namespace std;

//no copy,no assignment
//google 推荐写法
#define   DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)

class Test
{
public:
Test(){}
~Test(){}

private:
Test(const Test&t);
void operator=(const Test &t);
};

int main(int argc, const char *argv[])
{
Test t;

Test t2(t);//error

Test t3;
t3 = t; //error

return 0;
}


a):如果一个类,不需要复制和赋值,那就禁用这种能力,这可以帮助避免大量潜在的bug。
b):如果一个类,实现了像value一样的复制和赋值能力(意味着复制和赋值后,
两个对象没有任何关联,或者逻辑上看起来无任何关联),那么就称这个类的对象为  值语义(value semantics)。
如果类不能复制,或者复制后对象之间的资源归属纠缠不清,那么称为 对象语义(object semantics),或者 引用语义(reference semantics)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐