何时须要重载赋值操做符?
编译器是否提供默认的赋值操做符?ios
- 编译器为每一个类默认重载了赋值操做符
- 默认的赋值操做符仅完成浅拷贝
- 当须要进行深拷贝时必须重载赋值操做符
- 赋值操做符与拷贝构造函数有相同的存在乎义
test_1.cpp面试
#include <iostream> #include <string> using namespace std; class Test { int* m_pointer; public: Test() { m_pointer = NULL; } Test(int i) { m_pointer = new int(i); } void print() { cout << "m_pointer = " << hex << m_pointer << endl; } ~Test() { delete m_pointer; } }; int main() { Test t1 = 1; Test t2; t2 = t1; t1.print(); t2.print(); return 0; }
输出:【运行时发生内存错误】 m_pointer = 0x942f008 m_pointer = 0x942f008 *** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0942f008 *** ...... 已放弃 现象: t1.m_pointer 与 t2.m_pointer 指向同一片内存空间
修正: test_2.cpp编程
#include <iostream> #include <string> using namespace std; class Test { int* m_pointer; public: Test() { m_pointer = NULL; } Test(int i) { m_pointer = new int(i); } Test(const Test& obj) { m_pointer = new int(*obj.m_pointer); } Test& operator = (const Test& obj) { if( this != &obj ) { delete m_pointer; m_pointer = new int(*obj.m_pointer); } return *this; } void print() { cout << "m_pointer = " << hex << m_pointer << endl; } ~Test() { delete m_pointer; } }; int main() { Test t1 = 1; Test t2; t2 = t1; t1.print(); t2.print(); return 0; }
输出: m_pointer = 0x83ac008 m_pointer = 0x83ac018 现象: t1.m_pointer 与 t2.m_pointer 指向不一样的内存空间
重载赋值操做符,必然须要实现深拷贝!数组
Test& operator = (const Test& obj) { if( this != &obj ) { // ... } return *this; }
IntArray.happ
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; IntArray(int len); IntArray(const IntArray& obj); bool construct(); public: static IntArray* NewInstance(int length); int length(); bool get(int index, int& value); bool set(int index, int value); int& operator [] (int index); IntArray& operator = (const IntArray& obj); IntArray& self(); ~IntArray(); }; #endif
IntArray.cpp函数
#include "IntArray.h" IntArray::IntArray(int len) { m_length = len; } bool IntArray::construct() { bool ret = true; m_pointer = new int[m_length]; if( m_pointer ) { for(int i=0; i<m_length; i++) { m_pointer[i] = 0; } } else { ret = false; } return ret; } IntArray* IntArray::NewInstance(int length) { IntArray* ret = new IntArray(length); if( !(ret && (ret->construct())) ) { delete ret; ret = 0; } return ret; } int IntArray::length() { return m_length; } bool IntArray::get(int index, int& value) { bool ret = (index >= 0) && (index < length()); if( ret ) { value = m_pointer[index]; } return ret; } bool IntArray::set(int index, int value) { bool ret = (index >= 0) && (index < length()); if( ret ) { m_pointer[index] = value; } return ret; } int& IntArray::operator [] (int index) { return m_pointer[index]; } IntArray& IntArray::operator = (const IntArray& obj) { if( this != &obj ) { int* pointer = new int[obj.m_length]; if( pointer ) { for(int i=0; i<obj.m_length; i++) { pointer[i] = obj.m_pointer[i]; } m_length = obj.m_length; delete m_pointer; m_pointer = pointer; } } return *this; } IntArray& IntArray::self() { return *this; } IntArray::~IntArray() { delete[] m_pointer; }
main.cpp优化
#include <iostream> #include "IntArray.h" using namespace std; int main() { IntArray* a = IntArray::NewInstance(5); IntArray* b = IntArray::NewInstance(10); if( a && b ) { IntArray& array = a->self(); IntArray& brray = b->self(); cout << "array.length() = " << array.length() << endl; cout << "brray.length() = " << brray.length() << endl; array = brray; cout << "array.length() = " << array.length() << endl; cout << "brray.length() = " << brray.length() << endl; } delete a; delete b; return 0; }
输出: array.length() = 5 brray.length() = 10 array.length() = 10 brray.length() = 10
class T { };
==>this
class T { public: T(); T(const T&); T& operator = (const T&); ~T(); };
- 下面的代码输出什么?为何?
void code() { string s = "12345"; const char* p = s.c_str(); cout << p << endl; s.append("abcde"); cout << p << endl; }
#include <iostream> #include <string> using namespace std; int main() { string s = "12345"; const char* p = s.c_str(); cout << p << endl; s.append("abcde"); cout << p << endl; // p 成为了野指针! return 0; }
输出: 12345 12345
【string 对象内部维护了一个指向数据(位于堆空间中)的 char* 指针,这个指针可能在运行的过程当中发生改变】spa
s = "12345"; ==> s 对象在堆空间申请内存并将字符串"12345"拷贝进入,同时内部指针指向这一空间 (0xFF112233)
p = s.c_str(); ==> p 指针指向保存数据的内存空间 (0xFF112233)
s.append("abcde"); ==> s 对象从新在堆空间申请内存,并把两部分字符串拷贝进入,原有的堆空间被释放(0xFF112233),内部指针指向新的内存空间 (0xFF445566)指针
- 下面的程序输出什么?为何?
void code() { const char* p = "12345"; string s = ""; s.reserve(10); for(int i=0; i<5; i++) { s[i] = p[i]; } cout << "s.length : " << s.length() << endl; cout << "s.empty : " << s.empty() << endl; cout << "s : " << s << endl; }
#include <iostream> #include <string> using namespace std; int main() { const char* p = "12345"; string s = ""; s.reserve(10); // 将 s 的容量调整为 10 for(int i=0; i<5; i++) // 不要使用 C 语言中的方式操做 C++ 中的字符串 { s[i] = p[i]; } cout << "s.length : " << s.length() << endl; cout << "s.empty : " << s.empty() << endl; cout << "s : " << s << endl; return 0; }
输出: s.length : 0 s.empty : 1 s :
for 循环前:
m_cstr 指向10个字节的堆空间(0xFF010203)
m_length 字符长度为 0 (空串)
for 循环后:
m_cstr 指向 10 个字节的堆空间前 5 个字节发生改变(0xFF010203)
m_length 字符长度为 0 (空串)
警告:
在 C++ 中尽可能只采用 C++ 编程思想,而不采用混合方式开发,不然可能产生意想不到的问题
- 在须要进行深拷贝的时候必须重载赋值操做符
- 赋值操做符和拷贝构造函数有同等重要的意义
- string 类经过一个数据空间保存字符数据
- string 类经过一个成员变量保存当前字符串的长度
- C++ 开发时尽可能避开 C 语言中惯用的编程思想
以上内容参考狄泰软件学院系列课程,请你们保护原创!