[C++] Copy Control (part 1)
Copy, Assign, and Destroy
When we define a class, we specify what happens when objects of the class are copied, moved, assigned, and destroyed. A class controls these operations by defining five special member functions: copy contructor, copy-assignment contructor, move constructor, move-assignment operator and destructor.
The copy and copy-assignement define what happen when an object is initialized from another object of the same type.
The move and move-assignment define what happens when we assign an object of a class to another object of the same type.
The destructor define what happens when an object of the type cease to exist.
We refer to these operation as copy control
If a class does not define all of the copy-control members, compiler automatically define the missing operations.
A contructor is a copy contrucotr if the first parameter is a reference to the class type and additional parameters provide default value.
- class Foo{
- public:
- Foo(); // default constructor
- Foo(Const Foo&); // copy constructor
- };
When we do not define a copy constructor, compiler synthesize it for us. Unlike the default constructor, a copy construcotr is synthesized even if we define other constructor.
- string dots(, '.'); // direct constructor
- string s2 = dots; // copy constructor
- string null_book = "999-999-999"; // copy constructor
Copy initialization happens not only when we define a variable using an =, but also when we:
- pass an obejct as a argument to a parameter of a nonreference type
- Return an object from an function that has a nonreference return type
- Brace initialize the elements in an array or the members of an aggregate class.
- Sales_data trans, accm;
- trans = accm; // use the Sales_data copy assignment
- class Foo{
- public:
- Foo& operator=(const Foo&); // assignment operation
- //...
- };
Assignment operators ordinarily should return a reference to their left-hand operand.
Just as does for the copy constructor, the compiler generates synthesize copy assignment operator for a class if we do not define its own.
Generally, it assigns each members of the right-hand object to the corresponding members of the left-hand object using the copy-assignment operator of the type of the member.
- // equals to the synthesized copy-assignment operator
- Sales_data & Sales_data::operator=(const Sales_data &rhs){
- bookNo = rhs.bookNo; // call the string::operator=
- units_sold = rhs.units_sold; // use the built-in int assignment
- revenue = rhs.revenue;
- return *this;
- };
The Destructor is a member function with the same name of the type prefixed by the tilde(~). It has no return value and no parameter; there is always only one destructor for a given class.
- class Foo{
- ~Foo(); // destructor
- };
Unlike ordinarily pointers, the smart pointers are class types and have destructor.
- { // new scope
- // p and p2 point to dynamically allocated objects
- Sales_data *p = new Sales_data;
- auto *p2 = make_shared<Sales_data> (); // p2 is a shared_prt
- Sales_data item(*p2); // copy constructor copies *p into item
- vector<Sales_data> vec; // local variable
- vec.push_back(*p2); // copy the object to which p2 points
- delete p; // destructor called pointed to by p
- } // exit local scope; destructor called on item, p2, vec
- // destroy p2 decreases its use count; if the count goes to 0, the object is freeed
- // destroying vec destroy the elements in vec.
The only memory our code has to manage is the object we directly allocated. Our code frees only the dynamically allocated object bound to p.
The destructor is not run when a reference or pointer to a object go out of scope.
Generally, the synthesized destructor has an empty function body.
- class Sales_data{
- public:
- ~Sales_data() { }
- // ...
- }
If the class need a destrutor, it almost surely need the copy-assignment operator and copy construcotr.
Consider what would happen if we give HasPrt a destructor but use the synthesized copy-assignment operator and copy constructor.
- class HasPtr{
- public:
- HasPrt(const string & s = string()) : ps(new string(s)), i() { }
- ~HasPrt() { delete ps; }
- // WRONG: HasPtr need a copy constructor and copy-assignment
- // ...
- private:
- string * ps;
- };
The copy constructor and copy-assignment operator copy pointer member, meaning that multiple objects may be pointing to the same memory:
- HasPtr f(HasPtr hp){
- HasPtr ret = hp; // copy the given HasPtr
- return ret; // ret and hp are detroyed
- }
When f returns, both hp and ret are destroyed and HasPtr destructor run on each of these objects. These objects contain the same pointer value. This code will delete the pointer twice, this is an error.
- HasPtr p("some value");
- f(p); // When f complete, the memory to which ps points is freed
- HasPtr q(p); // now both p and q points to invalid memory
The second rule of thumb: If a class needs a copy constructor, it also almost surely needs a copy-assignement operator, and vice versa.
Nevertheless, needing either a copy constructor or a copy-assignment operator, does not indicate the need of destructor.
A deleted function is one that be declared but may not be used in any other way.
We can prevent copies by defining the copy constructor and copy-assignment operator as deleted functions, marked as =deleted.
- struct NoCopy{
- NoCopy = default;
- NoCopy(const NoCopy &) = delete; // no copy constructor
- NoCopy & operator = (const NoCopy &) = delete; // no copy-assignment operator
- ~NoCopy() = delete;
- // other members
- }
Although the primary use of deleted function is to suppress the control members, deleted functions are sometimes useful when we guide to function-matching process.
The Destructor should a deleted member.
We can dynamically allocate objects with deleted destructor, but we cannot free them.
It is not possible to define an object or delete a pointer of an object of a type with a deleted destructor.
If a class has a data member that cannot be default constructed, copied, assigned, destroyed, then its corresponding member will be a deleted function.
In essense, the copy-control members are synthesized as deleted function when it is impossible to copy, assign, or destroy a member of this class.
It should not be surprising that a class with a const members cannot use the synthesized copy-assignment construtor: after all, the operator attempt to assign to every member. It is not possible to assign a new value to const member.
If the copy-assignment operator is synthesized for a class with a reference member, the left-hand operand will continue to refer to the same object as it did before the assignment. This behaviour is unlikely to be desired. The synthesized copy-assignment operator is defined as deleted if the class has a reference member.
copy Control and Resource Management
In general, we have two choices: we can define copy operations to make a class behave like a value or like a pointer.
- Classes that behave like a value have their own state. The copy and original are independent.
- Classes that behave like a pointer share state. the copy and original use the same underlying data.
The IO types and unique_ptr do not allow copying or assignment, so they provide neither valuelike or pointerlike behavour.
What we do when we copy a pointer member determeines whether a class like HasPtr has valuelike or pointerlike behavours.
Classes that Act like Values
To provide valuelike behavour, each object has to have a copy of resources that the class manages. That means each objects of HasPtr has to have a copy of string to which ps points. To implement HasPtr needs:
- A copy constructor that copy the string, not just the pointer.
- A destructor to free the string
- A copy-assignment operator to free the existing string of left-hand operand, and copy the string from right-hand operand.
- class HasPtr{
- public:
- HasPtr(const string & s= string()) : ps(new string(s)), i() { }
- HasPtr(const HasPtr & p): ps(new string(*p.ps)), i(p.i) { }
- HasPtr & operator=(const HasPtr &);
- ~HasPtr() { delete ps; }
- private:
- string *ps;
- int i;
- };
Our code simple enough that we've define all but the copy-assignment operator in the class body.
Most copy-assignment should work with the destructor and copy constructor.
Valuelike Copy-Assignment Operator. For copy-assignment, it is crucially important that actions are done in sequence that is correct even if a class assign to itself.
- HasPtr & HasPtr::operator=(const HasPtr & rhs){
- auto newP = new string(rhs.ps); // copy the underlying string
- delete ps; // free the old memory
- ps = newp;
- i = rhs.i;
- return *this;
- };
Classes That Act Like Pointers
The easiest way to make a class like a pointer is to use share_ptrs to manage the resources in the classes.
However, sometimes we want to manage resources directly. In such case, it is useful to use a reference count. We will redefine HasPtr to provide pointerlike behavour, and we will do our won reference counting.
How can the copy and the orignal points to the same counter?
- The way to do that is to store the counter in dynamicac memory.
- Another way is to store the counter on a static member of the class (By Tony)
- class HasPtr{
- public:
- // constructor allocates a new string and a new counter, which is set to 1
- HasPtr(count string &s = string()): ps(new string(s)), i(), use(new size_t()) { }
- // copy constructor copies all data members, and increase the counter
- HasPtr(const HasPtr& p): ps(p.ps), i(p.i), use(p.use) { use++; }
- HasPtr& operator=(const HasPtr);
- ~HasPtr();
- private:
- string *Ps;
- int i;
- size_t *use; // member to keep trace of how many objects share *ps
- };
- HasPtr::~HasPtr(){
- --*use;
- if(*use == ){
- delete ps;
- delete use;
- }
- }
- HasPtr& HasPtr::Operator=(const HasPtr &rhs){
- ++*rhs.use;
- --*use;
- if(*use == ){
- delete ps;
- delete use;
- }
- ps = rhs.ps;
- i = rhs.i;
- use = rhs.use;
- return *this;
- }
Reference:
C++ Primer, Fifth Edition, chapter 13 Copy Control
[C++] Copy Control (part 1)的更多相关文章
- [c++] Copy Control
C++ allows the programmer to define how objects are to be copied, moved, assigned and destroyed. Tog ...
- Copy Control settings
Copy Control settings Skip to end of metadata Created by Rajesh Banka, last modified by Jyoti ...
- C/C++:copy control (拷贝控制)
前言:当定义一个类的时候,我们显示或者隐式地指定在此类型的对象拷贝,移动,赋值,销毁时做些什么,一个类通过定义五种特殊的成员函数来控制这些操作,包括拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值 ...
- C++之拷贝控制 (Copy Control)
只有2种成员 值成员: 指针成员: 依实现可分为raw pointer / shared_ptr; 现在,仅考虑第③种:资源对象共享 角度来考虑拷贝控制 类的两种语义:值语义.似指针 编译器提供的de ...
- 【C++ 补习】Copy Control
C++ Primer 5th edition, chapter 13. The Rule of Three If a class needs a destructor, it almost surel ...
- Bug 14143011 : ORA-19606: CANNOT COPY OR RESTORE TO SNAPSHOT CONTROL FILE
Bug 14143011 : ORA-19606: CANNOT COPY OR RESTORE TO SNAPSHOT CONTROL FILE [oracle@test]$ tail -f rma ...
- C++-copy constructor、copy-assignment operator、destructor
本文由@呆代待殆原创,转载请注明出处. 对于一个类来说,我们把copy constructor.copy-assignment operator.move constructor.move-assig ...
- [c++] Smart Pointers
内存管理方面的知识 基础实例: #include <iostream> #include <stack> #include <memory> using names ...
- code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类
0 C++中多态的概念 多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一.多态使得一个对象具有多个对象的属性.class Co ...
随机推荐
- you don't have permission to access ...........on this server问题解决
因为刚刚开始使用新架构的项目,于是把老项目统统替换成了新的项目.配置好后,本地登录页面报 you don't have permission to access ...... on this serv ...
- 集合Gk表示这样一堆数字,该集合内的数字有k个1
问题描述 集合Gk表示这样一堆数字,该集合内的数字有k个1.比如,G1 = { 1, 10, 100, 1000, ...} G2 = {11, 110, 1110 }, ... , Gk { ... ...
- 为什么有时候binlog文件会很大于max_binlog_size以及max_binlog_cache_size
问题描述 线上一个很有意思的现象,发现binlog文件大小是15G,查看了参数max_binlog_size是1073741824[1G], max_binlog_cache_size是2147483 ...
- 常用EL函数汇总 fn:contains ,fn:substring,fn:substringAfter...
由于在JSP页面中显示数据时,经常需要对显示的字符串进行处理,SUN公司针对于一些常见处理定义了一套EL函数库供开发者使用.这些EL函数在JSTL开发包中进行描述,因此在JSP页面中使用SUN公司的E ...
- laravel-admin 创建数据库并生成控制器
以user表为例 1. 生成迁移:php artisan make:migration create_users_table 在 database/migration 中生成迁移文件,可对迁移文件进行 ...
- flask(列表数据接口设计)
新闻列表数据只是当前页面的一部分 点击分类时需要去获取当前分类下的新闻数据 并在展示的时候需要更新新闻列表界面,不需要整体页面刷新 所以新闻数据也使用 ajax 的方式去请求后台接口进行获取 接口设计 ...
- Go搭建一个博客系统
go语言环境就不用多说了,版本肯定越高越好,这里用go1.10 先放着
- C语言实验报告(四)完全数
完全数,又称完美数或者完备数.是一些特殊的自然数.它所有的真因子的和,恰好等于它本身.编程找出1000以内的所有完全数,并输出该数成为完全数的因子. (例如6=1+2+3.按照6,its factor ...
- I2C软件模拟协议与电容触摸控制
I2C 与 Touch slide 最近做了一个与触摸滑条相关的测试,利用I2C通讯协议来配置触摸控制芯片的相关寄存器,读取触摸读数,并通过STM Studio动态显示触摸读数的变化过程.这个测试相对 ...
- 安装Flutter环境
mac 环境安装 系统需求 操作系统: macOS (64-bit) 硬盘: 700 MB 工具: bash, mkdir, rm, git, curl, unzip, which 环境安装 SDK ...