目录
基础知识
当类有自己的资源需要管理时,那么必须重写
-
析构函数
默认的析构函数不会释放资源 -
拷贝构造
默认的拷贝构造仅仅是“浅拷贝” -
拷贝赋值
默认的拷贝赋值仅仅是“浅拷贝”
有了左值和右值的区分,那么拷贝构造和拷贝赋值需要写两份
CBox(const CBox& other);
CBox(CBox&& other) noexcept;
CBox& operator= (const CBox& other);
CBox& operator= (CBox&& aBox) noexcept;
~CBox();
class rule_of_five
{
char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block
public:
rule_of_five(const char* s = "")
: cstring(nullptr)
{
if (s) {
std::size_t n = std::strlen(s) + 1;
cstring = new char[n]; // allocate
std::memcpy(cstring, s, n); // populate
}
}
~rule_of_five()
{
delete[] cstring; // deallocate
}
rule_of_five(const rule_of_five& other) // copy constructor
: rule_of_five(other.cstring)
{}
rule_of_five(rule_of_five&& other) noexcept // move constructor
: cstring(std::exchange(other.cstring, nullptr))
{}
rule_of_five& operator=(const rule_of_five& other) // copy assignment
{
return *this = rule_of_five(other);
}
rule_of_five& operator=(rule_of_five&& other) noexcept // move assignment
{
std::swap(cstring, other.cstring);
return *this;
}
// alternatively, replace both assignment operators with
// rule_of_five& operator=(rule_of_five other) noexcept
// {
// std::swap(cstring, other.cstring);
// return *this;
// }
};
(摘录)
c++,类的对象作为形参时一定会调用复制构造函数吗?
答:如果参数是引用传递,则不会调用任何构造函数;如果是按值传递,则调用复制构造函数,按参数的值构造一个临时对象,这个临时对象仅仅在函数执行是存在,函数执行结束之后调用析构函数。
如果类中没有定义复制构造函数 那对象就不能作为形参?
答:如果没有定义,编译器会自动为你定义一个,编译器自己定义的复制构造函数是按类成员变量的值复制的。有几个成员变量就重新创建一个对象,那个对象的成员变量和被复制的对象其成员变量的值相同。这里如果成员变量有指针的时候,就会出现多个指针指向同一个对象的问题。
unique_ptr删除拷贝语义
#include <iostream>
#include <memory>
using std::cout;
using std::endl;
/*
The first part - copyable/movable relates to the fact that with the simple - raw - pointer we can only shallow copy an object.
Of course, this happens in every case you have a pointer in your class
在类里写了一个裸指针,如果用编译器自动生成的拷贝/移动函数,都是浅拷贝。在处理不当的情况下,这个裸指针很可能称为空悬指针
目前有一种流行的想法:显式写了析构函数,那么其他4个函数都必须要写,要不就一个都不要显式的写出来
当然了,我个人认为没有必要,完美的事物会带来复杂性,而复杂性导致不可维护,这是一个trade-off问题
c++11后很少会用到裸指针,一般都是用智能指针,智能指针的话,一般用的是 std::unique_ptr
类的成员函数用到了 unique_ptr 后,编译器就没有了拷贝构造和拷贝赋值函数了
1. 如果这是需要的结果,这就是只有移动语义的类,类的对象间不能赋值
2. 如果这不是需要的结果,那么需要自己手写拷贝函数
写DLL时,一般用到 PIMPL 方法,用智能指针 std::unique_ptr
1. copyable and moveble class
2. noncopyble and movable class
当然也可以用 std::shared_ptr
*/
class Person
{
public:
Person() = default;
private:
std::unique_ptr<int> m_age;
};
int main(void)
{
Person p1;
Person p2;
p1 = p2;
/*
copy assignment operator of 'Person' is implicitly deleted because field 'm_age' has a deleted copy assignment operator
*/
return 0;
}
较好的写法
#include <iostream>
#include <memory>
#include <string>
using std::cout;
using std::endl;
class Person
{
public:
Person() = default;
void printMe(std::string aboutme)
{
cout << aboutme << endl;
}
private:
// 显式删除
Person(const Person &) = delete;
Person& operator=(const Person&) = delete;
private:
std::unique_ptr<int> m_age;
};
int main(void)
{
Person p;
p.printMe("this is a ");
return 0;
}
unique_ptr增加拷贝语义
#include <iostream>
#include <memory>
#include <string>
using std::cout;
using std::endl;
class Person
{
public:
explicit Person(int age) : m_age(new int(age))
{
}
~Person() = default;
Person(Person &&) noexcept = default;
Person& operator=(Person &&) noexcept = default;
public:
Person(const Person &other) : m_age(new int(*other.m_age))
{
}
Person& operator=(const Person& other)
{
if (this != &other)
{
m_age.reset(new int(*other.m_age));
}
return *this;
}
void PrintAge()
{
cout << "m_age: " << m_age << endl;
cout << "*m_age: " << *m_age << endl;
}
private:
std::unique_ptr<int> m_age;
};
int main(void)
{
Person p(10);
Person p1(100);
p.PrintAge();
p1.PrintAge();
p = p1;
p.PrintAge();
return 0;
}
/*
m_age: 0000022C095820B0
*m_age: 10
m_age: 0000022C09581F80
*m_age: 100
m_age: 0000022C09582010
*m_age: 100
*/
PIMPL
使用std::unique_ptr实现,当然也可以使用std::shared_ptr实现
无拷贝语义的PIMPL
- 构造的参数传给impl的构造函数
- 私有变量全部写在impl中
- 要实现的接口全在impl中
.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
class Person
{
public:
explicit Person(int age);
public:
// 实现全部在cpp里
~Person();
Person(Person &&) noexcept;
Person& operator=(Person &&) noexcept;
private:
// 删除拷贝语义
Person(const Person&) = delete;
Person &operator=(const Person&) = delete;
public:
// 暴露出的接口
void PrintMe();
private:
// Impl
class Impl;
std::unique_ptr<Impl> m_pimpl;
};
#endif // PERSON_H
.cpp
#include <iostream>
#include <string>
#include "person.h"
using std::cout;
using std::endl;
class Person::Impl
{
public:
Impl(int age) : m_age(age)
{
}
public:
~Impl() = default;
public:
void PrintMe()
{
cout << "in Impl " << m_age << endl;
}
private:
int m_age;
};
Person::Person(int age) : m_pimpl(new Impl(age))
{
}
void Person::PrintMe()
{
m_pimpl->PrintMe();
}
Person::~Person()=default;
Person::Person(Person &&other) noexcept = default;
Person& Person::operator=(Person &&other) noexcept = default;
.main
#include "person.h"
int main(void)
{
Person p(28);
p.PrintMe();
return 0;
}
有拷贝语义的PIMPL
.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
class Person
{
public:
explicit Person(int age);
public:
// 实现全部在cpp里
~Person();
Person(Person &&) noexcept;
Person& operator=(Person &&) noexcept;
public:
// 实现拷贝语义
Person(const Person&);
Person &operator=(const Person&);
public:
// 暴露出的接口
void PrintMe();
private:
// Impl
class Impl;
std::unique_ptr<Impl> m_pimpl;
};
#endif // PERSON_H
.cpp
#include <iostream>
#include <string>
#include "person.h"
using std::cout;
using std::endl;
class Person::Impl
{
public:
Impl(int age) : m_age(age)
{
}
public:
~Impl() = default;
public:
void PrintMe()
{
cout << "in Impl " << m_age << endl;
}
private:
int m_age;
};
Person::Person(int age) : m_pimpl(new Impl(age))
{
}
// 实现拷贝语义
Person::Person(const Person& other) : m_pimpl(new Impl(*other.m_pimpl))
{
}
Person& Person::operator=(const Person &other)
{
if (this != &other)
{
m_pimpl.reset(new Impl(*other.m_pimpl));
}
return *this;
}
void Person::PrintMe()
{
m_pimpl->PrintMe();
}
Person::~Person()=default;
Person::Person(Person &&other) noexcept = default;
Person& Person::operator=(Person &&other) noexcept = default;
.main
#include "person.h"
int main(void)
{
Person p(28);
p.PrintMe();
Person p1(50);
p1.PrintMe();
p = p1;
p.PrintMe(); // 50
return 0;
}
增加const语义
If you declare a method const then you cannot change members of the object.
In other words, they become const. But it’s a problem for our m_pImpl which is a pointer.
In a const method this pointer will also become const which means we cannot assign a different value to it…
but… we can happily call all methods of this underlying private class (not only constant)!.
非const对象,可以调用const函数
const对象,不可以调用非const函数
也就是说当写了一个const 对象后,如果不增加const函数,那么对象很可能无函数可调用了。
当然,现实场景可以是禁止写一个const对象,不负责const对象的函数调用
.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
class Person
{
public:
explicit Person(int age);
public:
// 实现全部在cpp里
~Person();
Person(Person &&) noexcept;
Person& operator=(Person &&) noexcept;
public:
// 实现拷贝语义
Person(const Person&);
Person &operator=(const Person&);
public:
// 暴露出的接口
void PrintMe();
void PrintMe() const;
private:
// Impl
class Impl;
// 实现const语义
const Impl* Pimpl() const { return m_pimpl.get(); }
Impl* Pimpl() { return m_pimpl.get(); }
std::unique_ptr<Impl> m_pimpl;
};
#endif // PERSON_H
.cpp
#include <iostream>
#include <string>
#include "person.h"
using std::cout;
using std::endl;
class Person::Impl
{
public:
Impl(int age) : m_age(age)
{
}
public:
~Impl() = default;
public:
// 实现const 语义
void PrintMe() const
{
cout << "in const in Impl " << m_age << endl;
}
void PrintMe()
{
cout << "not const in Impl " << m_age << endl;
}
private:
int m_age;
};
Person::Person(int age) : m_pimpl(new Impl(age))
{
}
// 实现拷贝语义
Person::Person(const Person& other) : m_pimpl(new Impl(*other.m_pimpl))
{
}
Person& Person::operator=(const Person &other)
{
if (this != &other)
{
m_pimpl.reset(new Impl(*other.m_pimpl));
}
return *this;
}
void Person::PrintMe()
{
Pimpl()->PrintMe();
}
// 实现const语义
void Person::PrintMe() const
{
Pimpl()->PrintMe();
}
Person::~Person()=default;
Person::Person(Person &&other) noexcept = default;
Person& Person::operator=(Person &&other) noexcept = default;
.main
#include "person.h"
int main(void)
{
Person p(28);
p.PrintMe();
const Person p1(50);
p1.PrintMe();
return 0;
}