聊一聊类的创建(cpp)
C++对比于C最重要的补充就是增加了类的功能。对于类的创建你又了解多少呢。在C++11的标准下怎样漂亮的创建一个类呢。赶紧来看看这篇文章里有没有你还不知道的知识点。
类的默认函数
一个普通的类编译器会在需要的时候默认生成6个函数
- 默认构造
- 默认拷贝构造
Empty(const Empty &tmp)
- 默认析构
- 默认赋值
Empty& operator=(const Empty &rhs); //赋值运算符
- 取地址运算符
Empty* operator&(); //取址运算符
- 取地址运算符const
const Empty* operator&() const; //取址运算符(const版本)
类的扩展
一个class如果包含了普通指针成员的话那就必须实现下列函数,特别是赋值函数和移动构造函数不要忘记。(如果是智能指针则可以不实现)
- 构造函数
- 析构函数
- 拷贝构造
- 移动构造
&&版本的拷贝构造
- 赋值函数
=赋值运算符
- 移动赋值
&&版本的=赋值运算符
上述缺少一个都会造成内存泄漏;=delete =default 关键字一般只会用在上述几个函数,普通函数几乎不用。
tips:拷贝构造和赋值函数还必须检查是否是自己给自己拷贝
拷贝构造和赋值函数的区别
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
- 一个对象以值传递的方式传入函数体
- 一个对象以值传递的方式从函数返回
- 一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数会完成对象之间的位拷贝。位拷贝又称浅拷贝。例子:
例子: String a("hello"); //构造函数 String b("world"); //构造函数 String c = a; // 调用了!拷贝!构造函数,最好写成 c(a); c = b; // 调用了!赋值!函数。因为c是已经存在创建好的。
规律
- 对象不存在,且没用别的对象来初始化,就是调用了构造函数;
- 对象不存在,且用别的对象来初始化,就是拷贝构造函数(上面说了3中情况)
- 对象存在,用别的对象来给它赋值,就是赋值函数。
赋值函数的注意事项
- 返回值类型声明为该类型的引用。否则会多出来一个因拷贝构造诞生的临时实例。`A& operator=(const A &temp)`
- 返回是实例自身的引用(*this),只有返回引用才能连续赋值。(str1=str2=str3)
- 传入参数声明为常量引用。不声明引用从形参到实参会拷贝构造,这样就无限循环了。const主要为了安全
- 是否释放内存?看需要
- 判断传入自身
- 如果是同一个对象则不进行赋值操作,直接返回。
- 如果事先不判断就进行赋值,那么在释放自身的内存时就会导致严重的问题:当*this和传入的参数是同一个实例时,那么一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此找不到需要赋值的内容了。
完整实例代码
#include<iostream> #include<list> using namespace std; class A { private: int a; int *buf; public: A(){//构造 a = 0; buf = new int[100]; cout << "A" << endl; } ~A(){//析构 if (buf != nullptr) { delete[]buf; cout << "~A" << endl; } } A(const A &tmp)//拷贝构造函数 //= delete; { if(this!=&tmp)//检查自己拷贝自己 { this->a = tmp.a; this->buf = new int[100]; cout << "copy A" << endl; } } A( A &&tmp)//移动构造 { cout << "move A" << endl; this->a = tmp.a; this->buf = tmp.buf; tmp.buf = nullptr; } A& operator=(const A &temp)//赋值函数 // = delete; { this->a = temp.a; this->buf = new int[100]; cout << "= A" << endl; return *this; } A operator=( A &&temp)//移动赋值 { cout << "move = A" << endl; this->a = temp.a; this->buf = temp.buf; temp.buf = nullptr; // 置为空指针 return std::move(*this);//变量强制转右值 } A copyA(A&a) { return std::move(a); } }; int main() { list<A> list_; for (auto a : { 1, 2, 4 }) { cout << "-------begin------" << endl << endl; list_.push_back(A()); } cout << "-------1------" << endl; list_.clear(); cout << "-------2------" << endl; { A a; A b = a; const A c; A d = c; } getc(stdin); return 1; } 输出 -------begin------ A move A -------begin------ A move A -------begin------ A move A -------1------ ~A ~A ~A -------2------ A copy A A copy A ~A ~A ~A ~A
智能指针代码
#include<iostream> #include<list> #include<memory> using namespace std; int cnt = 0; //记录C的分配析构次数 class C { public: int c; public: C(int aa) :c(aa) { cnt++; cout << "C" << endl; } ~C() { cnt--; cout << "~C" << endl; } C(const C&) { cout << "C copy" << endl; cnt++; } }; int a = 0; //记录B的分配析构次数 class B { public: std::shared_ptr<C> buf = nullptr; public: B() { buf = make_shared<C>(3); cout << "B" << endl; a++; } ~B() { if (buf != nullptr) { a--; cout << "~B" << endl; } } B(const B& tmp) //= delete; { this->buf = make_shared<C>(*tmp.buf); a++; cout << "copy B" << endl; //return *this; } B(B&& tmp) { cout << "move B" << endl; this->buf = tmp.buf; tmp.buf = nullptr; } B& operator=(const B& temp)// = delete; { a++; this->buf = make_shared<C>(*temp.buf); cout << "= B" << endl; return *this; } B&& operator=(B&& temp) { cout << "move = B" << endl; this->buf = temp.buf; temp.buf = nullptr; return std::move(*this); } }; int main() { list<B> list_; for (auto a : { 1, 2, 4 }) { cout << "-------begin------" << endl << endl; list_.push_back(B()); } cout << "-------1------" << endl; list_.clear(); cout << "-------2------" << endl; { B a; B b = a; } cout << "a = " << a << "cnt = " << cnt << endl; getc(stdin); return 1; } 结果: -------begin------ C B move B -------begin------ C B move B -------begin------ C B move B -------1------ ~B ~C ~B ~C ~B ~C -------2------ C B C copy copy B ~B ~C ~B ~C a = 0cnt = 0#C/C++##秋招##内推##提前批##面经#