精华内容
下载资源
问答
  • 通过容器emplace_back传参后,调用构造函数,将构造函数生成的对象存入到容器中。打印出容器的内容,此时容器branches_[0]中m_a,m_b,m_c的值为1, 2, 3。 接着调用赋值构造函数,生成对象bat2。在构造函数中定义m_a...

    先分享这个可不思议的结果,再来分析为什么会这样。

    情况是:实例化Branch类为bat1,给bat1的构造函数传递参数,将private的成员变量m_a,m_b,m_c分别赋值为1,2, 3。通过容器emplace_back传参后,调用构造函数,将构造函数生成的对象存入到容器中。打印出容器的内容,此时容器branches_[0]中m_a,m_b,m_c的值为1, 2, 3。

    接着调用赋值构造函数,生成对象bat2。在构造函数中定义m_a,m_b,m_c的值为4, 5, 6。再通过容器emplace_back将对象bat2存入到容器中。最后打印出令人有点吃惊的结果:branches_[0] = {4,5,6}, branches_[1] = {4,5,6}。在这里先提几个问题:

    1. 这个是浅拷贝还是深拷贝,bat1中m_a, m_b, m_c的值是否还是1, 2, 3
    2. 之前压入容器中的1, 2, 3中的值为什么会变成4, 5, 6?
    3. emplace_back和push_back有什么区别?
    #include<iostream>
    #include<vector>
    using namespace std;
    class Branch{
        private:
            int m_a;
            int m_b;
            int m_c;
        public:
            Branch(int a, int b, int c);      // 构造函数
            Branch(const Branch &a);    // 拷贝构造函数
            void Bond();  // Bond主要完成将m_a,m_b, m_c这三个变量压入到容器中
            void show();   // 将容器中的值打印出来
    };
    vector<Branch> branches_;
    // 容器branches_是Branch类,默认会调用Branch的构造函数参数为单位往容器中压入元素
    
    Branch::Branch(int a, int b ,int c) : m_a(a), m_b(b), m_c(c){
        cout <<"hi" << endl;  // 通过构造函数列表进行初始化private变量,等同于m_a = a, m_b = b, m_c = c
    }// 每次调用构造函数的时候都打印出一个“hi”
    
    Branch::Branch(const Branch &a){
        this->m_a = 4;  // 定义赋值构造函数,并改变变量的值
        this->m_b = 5; 
        this->m_c = 6;
        cout << "copy_copy" << endl;  // 一旦出现copy_copy就说明调用了构造函数
    }
    
    void Branch::Bond(){  // 调用构造函数将对象压入到容器中
        branches_.emplace_back(m_a, m_b, m_c);
    }
    
    void Branch::show(){ // 将容器中的值打印出来,此处默认直接将容器中[0][1]的值都打印出来,也可以使用迭代器
        cout << "m_a = " << m_a << endl;  // 打印出实例化对象中变量m_a的值
        for(int i = 0; i < 2; i++){
                cout << "branches_["  << i << "]" << ".m_a = " << branches_[i].m_a << endl;
                cout << "branches_["  << i << "]" << ".m_b = " << branches_[i].m_b << endl;
                cout << "branches_["  << i << "]" << ".m_c = " << branches_[i].m_c << endl;
        }
    }
    
    int main(){
        Branch bat1(1, 2, 3);  // 实例化对象,调用构造函数并初始化成员变量
        bat1.Bond();  // 将bat1的三个成员变量压入到容器中
        bat1.show();  // 打印容器中的内容
    
        Branch bat2 (bat1);  // 调用赋值构造函数,实例化为bat2
        bat2.Bond();
        bat2.show();
        return 0;
    }
    

    之前压入容器中的123变成了456

    什么是vector

    vector是c++相比于c非常重要的一个改变,在c中定义一个数组必须制定数组的长度,在后续使用的过程中没办法随意缩减或者增加,而c++中vector可以不停地往这个容器中添加元素。

    在android art源码中,很容易看到基于类的容器比如上述代码中:vector<Branch> branches_;表示这个是类的容器,我们可以用二维数组的方式进行理解,branches_[],‘[]’表示的是有多个对象,每个组中分别是构造函数的参数的个数,在android这样大型的项目中,可能还会涉及到构造函数的重载和构造函数的参数初始化。比如:
    容器的定义
    上图展示的就是在art中关于branches_容器的定义,容器为类容器,给容器中压入参数是以构造函数为参数的类型重载匹配后,调用构造函数,在容器中压入的构造函数生成的对象,该对象中有类的所有成员变量,因为对象是由成员变量和成员函数组成的,而成员函数在内存中不占空间,但是成员变量是占据空间的

    容器的定义也可以有以下几种:

    void test01() {
    	vector<int>v1;//默认构造,无参构造
    
    	for (int i = 0; i < 10; i++) {
    		v1.push_back(i);
    	}
    	printVector(v1);
    	//通过区间方式进行构造
    	vector<int>v2(v1.begin(), v1.end());
    	printVector(v2);
    
    	//n个elem方式构造
    	vector<int>v3(10, 100);
    	printVector(v3);
    
    	//拷贝构造
    	vector<int >v4(v3);
    	printVector(v4);
    }
    
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    100 100 100 100 100 100 100 100 100 100 
    100 100 100 100 100 100 100 100 100 100 
    

    容器的内容的获取与打印

    容器中压入参数的方式有push_back和emplace_back两种。获取容器中内容的方式有四种,此处针对类的容器进行分析。

    第一种:直接像二维数组一样直接获取容器中的元素,如:branches_[i].m_a

    第二种:(*branches_.begin()).m_b,branches_.begin()获得是的容器首元素的地址,解引用后得到branches_[0]。这种情况和二维数组是一样的,帮大家复习一下:

    #include<iostream>
    
    using namespace std; 
    
    int main(){
        int a[][3] = {{1,2,3},{4,5,6},{7,8,9}};
        cout << "a: " << a << endl;
        cout << "a[0]: " << a[0] << endl;
        cout << "*a: " << *a << endl;
        cout << "(*a)[0]: " << (*a)[0] <<endl;
        return 0; 
    }
    
    a: 0x7ffef7160e60  // 直接用二维数组的返回的是首元素的地址
    a[0]: 0x7ffef7160e60 // a[0]也是首元素的地址
    *a: 0x7ffef7160e60  // 但是对a表示的地址进行解引用,得到的不是首元素,而是a[0],还是一个地址,只是表示的是第一个数组的地址
    (*a)[0]: 1 // 对a的地址解引用得到的是一个数组的地址,对首地址进行解引用,获取第0个元素的内容就等于为a[0][0],就可以得到元素1
    

    同样branches_.end()可以获取容器中末尾元素的地址。

    第三种:branches_.back().m_b可以获得末尾元素的引用,branches_.front().m_b可以获得首元素的引用。

    第四种: 利用迭代器获取容器中的元素,用法为:

     for(vector<Branch>::iterator it = branches_.begin(); it != branches_.end(); it++){
            cout << (*it).m_a << "  " <<(*it).m_b <<  "  " <<(*it).m_c << "  " << "\n";
        }
    

    需要注意的是,如果在循环中打印it的值,指向的永远是一个地址,即迭代器本身的地址是不会变的。

    #include<iostream>
    #include<vector>
    using namespace std;
    class Branch{
        private:
            int m_a;
            int m_b;
            int m_c;
        public:
            Branch(int a, int b, int c);
            void Bond();
            void show();
    };
    vector<Branch> branches_;
    
    Branch::Branch(int a, int b ,int c) : m_a(a), m_b(b), m_c(c){
    }
    
    void Branch::Bond(){
        branches_.emplace_back(m_a, m_b, m_c);
        cout << "branches_.size() = " <<  branches_.size() << endl;
        cout << "branches_.back().m_b = " << branches_.back().m_b << endl;
        cout << "(*branches_.begin()).m_b = " <<  (*branches_.begin()).m_b << endl;
        cout << "branches_[0].m_c = " << branches_[0].m_c << endl;
    }
    
    void Branch::show(){
        for(vector<Branch>::iterator it = branches_.begin(); it != branches_.end(); it++){
            cout << (*it).m_a << "  " <<(*it).m_b <<  "  " <<(*it).m_c << "  " << "\n";
        }
    }
    
    int main(){
        Branch bat1(1,2,3);
        bat1.Bond();
        bat1.show();
    
        cout << "=============" << endl;
    
        Branch bat2(4,5,6);
        bat2.Bond();
        bat2.show();
    
        return 0;
    }
    
    
    branches_.size() = 1
    branches_.back().m_b = 2
    (*branches_.begin()).m_b = 2
    branches_[0].m_c = 3
    1  2  3  
    =============
    branches_.size() = 2
    branches_.back().m_b = 5
    (*branches_.begin()).m_b = 2
    branches_[0].m_c = 3
    1  2  3  
    4  5  6 
    

    容器中压入的是参数还是参数的地址?

    回到文章开头,为什么容器中的值123会变成456,会不会是因为bat1中的值发生了变化,容器存放的是不是变量的地址,然后发生的改变呢?那么我们来做个实验:

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main(){
        int a = 1, b = 2, c = 3;
    
        vector<int>D;
        D.emplace_back(a);
        D.emplace_back(b);
        D.emplace_back(c);    
        for(vector<int>::iterator it = D.begin(); it != D.end(); it++){
            cout << *it  << " "<< endl;
        }
        cout << "======" << endl;
        a = 100;
        D.emplace_back(a);
        for(vector<int>::iterator it = D.begin(); it != D.end(); it++){
            cout << *it  << " "<< endl;
        }
        return 0;
    }
    
    1 
    2 
    3 
    ======
    1 
    2 
    3 
    100 
    

    先定义了三个int的元素,然后存放到容器中,接着对变量a的值进行改变,从1变成100,这个时候再将a存入到容器中,分别打印之前容器中的值和之后容器中的值。可以发现:变量a在容器中的值1并没有发生改变,改变变量a推入容器后,只是在容器后继续存入了值为100的数。所以容器其实就是数组,你给数组中a[0]赋值,那么就是在一块新开辟的空间中存入这个数。

    所以导致文章开头容器中的值发生改变的根本原因并不是因为bat1中成员变量的值发生了改变,也不是容器中存放的是成员变量的地址。接着我们来讨论一下是什么原因让我们怀疑bat1中成员变量的值可能发生改变?

    浅拷贝与深拷贝

    通俗的来讲,浅拷贝就是不定义拷贝构造函数进行的拷贝,拷贝出来的对象与之前的对象其实指向的是同一块内存空间,这个与fork的原理有些类似。在没有定义拷贝构造函数而进行拷贝的时候,编译器会自动调用默认拷贝构造函数进行浅拷贝。

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    class Array{
    public:
        Array(int len);
        ~Array();
    public:
        int operator[](int i) const { return m_p[i]; }  //获取元素(读取)
        int &operator[](int i){ return m_p[i]; }  //获取元素(写入)
        int length() const { return m_len; }
    private:
        int m_len;
        int *m_p;
    };
    Array::Array(int len): m_len(len){
        m_p = (int*)calloc(len, sizeof(int) );
    }
    Array::~Array(){ free(m_p); }
    //打印数组元素
    void printArray(const Array &arr){
        int len = arr.length();
        for(int i=0; i<len; i++){
            if(i == len-1){
                cout<<arr[i]<<endl;
            }else{
                cout<<arr[i]<<", ";
            }
        }
    }
    int main(){
        Array arr1(10);
        for(int i=0; i<10; i++){
            arr1[i] = i;
        }
       
        Array arr2 = arr1;
        arr2[5] = 100;
        arr2[3] = 29;
       
        printArray(arr1);
        printArray(arr2);
       
        return 0;
    }
    0, 1, 2, 29, 4, 100, 6, 7, 8, 9
    0, 1, 2, 29, 4, 100, 6, 7, 8, 9
    

    可以发现在浅拷贝的时候,arr2的成员变量内容的修改也会导致arr1成员变量的修改。

    但如果一个类拥有指针类型的成员变量或者需要在创建对象时对成员变量进行预处理,比如次数统计,时间等就需要对拷贝构造函数进行自定义,这个时候需要进行深拷贝。在深拷贝的之后,拷贝出的对象与原对象拥有不同的内存空间,二者之间彼此独立。

    反观文章开头的例子,由于我们定义了拷贝构造函数并给成员变量进行了重新赋值,那么bat2的拷贝是一个深拷贝,也就是说bat1中的成员变量中的值并不会发生改变。如果在bat2.show()显示完之后再调用bat1.show(),打印出的m_a的值并没有发生变化,还是1。
    浅拷贝与深拷贝

    vector内容发生改变的原因——容器扩容

    在进行了多次尝试之后,如果给容器初始化的时候就给定一个足够大小的空间,该现象就不会出现。
    给容器初始化一个空间

    由于一开始并没有给容器制定空间,默认初始大小为1,当拷贝构造函数bat2继续往容器中添加时,容器的内存不够了,需要*2的方式进行扩容。在扩容的过程中,vector需要把旧值拷贝到新的空间中,在这个过程中会调用到构造函数与拷贝构造函数,于是容器中的emplace.back就直接把拷贝构造函数塞进容器中了,但是注意,由于还是定义了拷贝构造函数并赋值了,此处是深拷贝,bat1中的成员变量的值不会改变,而是在vector扩容的过程中自动调用了拷贝构造函数将本来要塞进容器中的对象bat1变成了拷贝出的bat2。
    vector扩容时调用了拷贝构造函数

    vector——emplace.back与push.back

    emplace.back不会调用拷贝构造函数,但是push.back会。假如我们往容器中添加对象的方式是push.back,那么即便容器中的大小已经初始化成足够大的大小,容器中存放的对象却是拷贝构造函数对象。
    push.back会调用拷贝构造函数,将拷贝构造函数对象存入到容器中

    总结

    这个例子是在学习Android art代码的时候自己敲出来的例子,虽然用法有点不太正规,但是很有趣,可以学习到类,成员变量,成员函数,容器,类容器,打印容器中的内容,构造函数,赋值构造函数,构造函数重载,浅拷贝深拷贝,容器扩容等知识。简单,但涵盖的面很多。

    展开全文
  • C++拷贝构造函数、移动构造函数

    首先吐槽下,自己一开始搜索的时候,几乎网上所有的都是抄某一个人的,所以自己不得不自己做实验

    (1)此时p不是将亡值,所以push_back调用拷贝构造函数

    #include <vector> 
    #include <iostream> 
    
    using namespace std;
    
    class Person {
    public:
        Person(string name,int age):name(name),age(age){
            cout << "I have been created" << endl;
        }
        Person(const Person &p):name(p.name),age(p.age) {
            cout << "I have been copy created" << endl;
        }
        Person(Person &&p) :name(p.name),age(p.age) {
            cout << "I have been moved" << endl;
        }
        ~Person() {
    		cout<<"I have been destroyed"<<endl;
    	}
    private:
    	string name;
        int age;
    };
    
    int main()
    {
        cout << "emplace_back:" << endl;
        vector<Person> v1;
        v1.emplace_back("sunxu",24);  
    
    	vector<Person> v2;
        cout << "push_back:" << endl;
        Person p=Person("sunxu1",24);
        v2.push_back(p);
        //v1.push_back(Person("sunxu2",24));
        //v1.push_back(Person("sunxu2",24));
        //v1.push_back(Person("sunxu2",24));
        
        cout<<"destroy:"<<endl;
        return 0;
    }
    
    

    (2)修改main代码如下:

    int main() {
        cout << "emplace_back:" << endl;
        vector<Person> v1;
        v1.emplace_back("sunxu",24);  
    
        vector<Person> v2;
        cout << "push_back:" << endl;
        //Person p=Person("sunxu1",24);
        //v2.push_back(p);
        v2.push_back(Person("sunxu2",24));
        //v1.push_back(Person("sunxu2",24));
        //v1.push_back(Person("sunxu2",24));
        
        cout<<"destroy:"<<endl;
        return 0;
    }

    此时传入的是一个临时对象(将亡值),调用移动构造函数,同时调用析构函数释放临时对象。

    (3)接下来考虑扩容问题,修改main如下:

    int main() {
        cout << "emplace_back:" << endl;
        vector<Person> v1;
        v1.emplace_back("sunxu",24);  
    
        vector<Person> v2;
        cout << "push_back:" << endl;
        //Person p=Person("sunxu1",24);
        //v2.push_back(p);
        cout<<"fisrt push back"<<endl;
        v1.push_back(Person("sunxu2",24));
        cout<<"second push back"<<endl;
        v1.push_back(Person("sunxu2",24));
        cout<<"third push back"<<endl;
        v1.push_back(Person("sunxu2",24));
        
        cout<<"destroy:"<<endl;
        return 0;
    }

    因为扩容,所以需要拷贝构造之前的元素,调用了对应次数的拷贝构造函数,同时释放之前的资源。但是第三次push_back,因为此时空间可以新增一个元素,所以就没有开辟新内存空间,调用拷贝构造函数并且释放资源。

     

    展开全文
  • C++ 语言拷贝构造函数、拷贝赋值运算符和析构函数 每个类都定义了一个新类型和在此类型对象上可执行的操作。类可以定义构造函数,用来控制在创建此类型对象时做什么。一个类通过定义五种特殊的成员函数来控制这些...

    C++ 语言拷贝构造函数、拷贝赋值运算符和析构函数

    每个类都定义了一个新类型和在此类型对象上可执行的操作。类可以定义构造函数,用来控制在创建此类型对象时做什么。一个类通过定义五种特殊的成员函数来控制这些操作,拷贝构造函数 (copy constructor)、拷贝赋值运算符(copy-assignment operator)、移动构造函数 (move constructor)、移动赋值运算符 (move-assignment operator) 和析构函数 (destructor)。拷贝和移动构造函数定义了当用同类型的另一个对象初始化本对象时做什么。拷贝和移动赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么。析构函数定义了当此类型对象销毁时做什么。我们称这些操作为拷贝控制操作 (copy control)。

    如果一个类没有定义所有这些拷贝控制成员,编译器会自动为它定义缺失的操作。在定义任何 C++ 类时,拷贝控制操作都是必要部分。如果我们不显式定义这些操作,编译器也会为我们定义,但编译器定义的版本的行为可能并非我们所想。

    1. 拷贝构造函数、拷贝赋值运算符和析构函数

    1.1 拷贝构造函数

    如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。

    class Foo {
    public:
    	Foo();  // 默认构造函数
    	Foo(const Foo &);  // 拷贝构造函教
    	// ...
    };
    

    拷贝构造函数的第一个参数必须是一个引用类型。虽然我们可以定义一个接受非 const 引用的拷贝构造函数,但此参数几乎总是一个 const 的引用。拷贝构造函数在几种情况下都会被隐式地使用。拷贝构造函数通常不应该是 explicit 的。

    1.2 合成拷贝构造函数

    如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个。与合成默认构造函数不同,即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数。对某些类来说,合成拷贝构造函数 (synthesized copy constructor) 用来阻止我们拷贝该类类型的对象。而一般情况,合成的拷贝构造函数会将其参数的成员逐个拷贝到正在创建的对象中。编译器从给定对象中依次将每个非 static 成员拷贝到正在创建的对象中。

    每个成员的类型决定了它如何拷贝:对类类型的成员,会使用其拷贝构造函数来拷贝;内置类型的成员则直接拷贝。虽然我们不能直接拷贝一个数组,但合成拷贝构造函数会逐元素地拷贝一个数组类型的成员。如果数组元素是类类型,则使用元素的拷贝构造函数来进行拷贝。

    Sales 类的合成拷贝构造函数等价于:

    class Sales {
    public:
    	// 与合成的拷贝构造函数等价的拷贝构造函数的声明
    	Sales(const Sales &);
    private:
    	std::string book_no;
    	int sold_num = 0;
    	double revenue = 0.0;
    };
    
    // 与 Sales 的合成的拷贝构造函数等价
    Sales::Sales(const Sales &orig) :
    	book_no(orig.book_no),  // 使用 string 的拷贝构造函数
    	sold_num(orig.sold_num),  // 拷贝 orig.sold_num
    	revenue(orig.revenue)  // // 拷贝 orig.revenue
    {}  // 空函数体
    
    

    1.3 拷贝初始化

    直接初始化和拷贝初始化之间的差异:

    std::string dots(10, '.');  // 直接初始化
    std::string s1(dots);  // 直接初始化
    std::string s2 = dots;  // 拷贝初始化
    std::string null_book = "9-99999-9";  // 拷贝初始化
    std::string nines = std::string(100, '9');  // 拷贝初始化
    

    当使用直接初始化时,我们实际上是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。当我们使用拷贝初始化 (copy initialization) 时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换。

    拷贝初始化通常使用拷贝构造函数来完成。如果一个类有一个移动构造函数,则拷贝初始化有时会使用移动构造函数而非拷贝构造函数来完成。现在,我们只需了解拷贝初始化何时发生,以及拷贝初始化是依靠拷贝构造函数或移动构造函数来完成的就可以了。

    拷贝初始化不仅在我们用 = 定义变量时会发生,在下列情况下也会发生:

    • 将一个对象作为实参传递给一个非引用类型的形参。
    • 从一个返回类型为非引用类型的函数返回一个对象。
    • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员。

    某些类类型还会对它们所分配的对象使用拷贝初始化。当我们初始化标准库容器或是调用其 insertpush 成员时,容器会对其元素进行拷贝初始化。与之相对,用 emplace 成员创建的元素都进行直接初始化。

    1.4 参数和返回值

    在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。当一个函数具有非引用的返回类型时,返回值会被用来初始化调用方的结果。

    拷贝构造函数被用来初始化非引用类类型参数,这一特性解释了为什么拷贝构造函数自己的参数必须是引用类型。如果其参数不是引用类型,则调用永远也不会成功。为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。

    1.5 拷贝初始化的限制

    如果我们使用的初始化值要求通过一个 explicit 的构造函数来进行类型转换,那么使用拷贝初始化还是直接初始化就不是无关紧要.

    std::vector<int> vl(10);  // 正确:直接初始化
    std::vector<int> v2 = 10;  // 错误:接受大小参教的构造函数是 explicit 的 
    void f(std::vector<int>);  // f 的参数进行拷贝初始化
    f(10);  // 错误:不能用一个 explicit 的构造函数拷贝一个实参
    f(std::vector<int>(10));  // 正确:从一个 int 直接构造一个临时 vector
    

    直接初始化 v1 是合法的,但看起来与之等价的拷贝初始化 v2 则是错误的,因为 std::vector 的接受单一大小参数的构造函数是 explicit 的。当传递一个实参或从函数返回一个值时,我们不能隐式使用一个 explicit 构造函数。如果我们希望使用一个 explicit 构造函数,就必须显式地使用。

    1.6 编译器可以绕过拷贝构造函数

    在拷贝初始化过程中,编译器可以 (但不是必须) 跳过拷贝/移动构造函数,直接创建对象。即,编译器被允许将下面的代码

    std::string null_book = "9-99999-9";  // 拷贝初始化
    

    改写为

    std::string null_book("9-99999-9");  // 编译器略过了拷贝构造函数
    

    但是,即使编译器略过了拷贝/移动构造函数,但在这个程序点上,拷贝/移动构造函数必须是存在且可访问的 (不能是 private 的)。

    2. 拷贝赋值运算符

    类也可以控制其对象如何赋值:

    Sales trans, accum;
    trans = accum; // 使用 Sales 的拷贝赋值运算符
    

    与拷贝构造函数一样,如果类未定义自己的拷贝赋值运算符,编译器会为它合成一个。

    2.1 重载赋值运算符

    重载运算符 (overloaded operator) 本质上是函数,其名字由 operator 关键字后接表示要定义的运算符的符号组成。赋值运算符就是一个名为 operator= 函数。类似于任何其他函数,运算符函数也有一个返回类型和一个参数列表。

    重载运算符的参数表示运算符的运算对象,赋值运算符必须定义为成员函数。如果一个运算符是一个成员函数,其左侧运算对象就绑定到隐式的 this 参数。对于一个二元运算符赋值运算符,其右侧运算对象作为显式参数传递。

    拷贝赋值运算符接受一个与其所在类相同类型的参数:

    class Foo {
    public:
    	Foo& operator= (const Foo &);  // 赋值运算符
    	// ...
    };
    

    为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。标准库通常要求保存在容器中的类型要具有赋值运算符,且其返回值是左侧运算对象的引用。赋值运算符通常应该返回一个指向其左侧运算对象的引用。

    2.2 合成拷贝赋值运算符

    如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符 (synthesized copy-assignment operator)。对于某些类,合成拷贝赋值运算符用来禁止该类型对象的赋值。如果拷贝赋值运算符并非出于此目的,它会将右侧运算对象的每个非 static 成员赋予左侧运算对象的对应成员,这一工作是通过成员类型的拷贝赋值运算符来完成的。对于数组类型的成员,逐个赋值数组元素。合成拷贝赋值运算符返回一个指向其左侧运算对象的引用。

    下面的代码等价于 Sales 的合成拷贝赋值运算符:

    // 等价于合成拷贝赋值运算符
    Sales &Sales::operator=(const Sales &rhs) {
    	book_no = rhs.book_no;  // 调用 string::operator=
    	sold_num = rhs.sold_num;  // 使用内置的 int 赋值
    	revenue = rhs.revenue;  // 使用内置的 double 赋值
    	return *this;  // 返回一个此对象的引用
    }
    

    3. 析构函数

    析构函数执行与构造函数相反的操作:构造函数初始化对象的非 static 数据成员,还可能做一些其他工作。析构函数释放对象使用的资源,并销毁对象的非 static 数据成员。析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数。由于析构函数不接受参数,因此它不能被重载。对一个给定类,只会有唯一一个析构函数。

    class Foo {
    public:
    	~Foo();  // 析构函数
    	// ...
    };
    

    3.1 析构函数完成什么工作

    析构函数有一个函数体和一个析构部分。在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化。在一个析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序的逆序销毁。在对象最后一次使用之后,析构函数的函数体可执行类设计者希望执行的任何收尾工作。通常,析构函数释放对象在生存期分配的所有资源。

    在一个析构函数中,不存在类似构造函数中初始化列表的东西来控制成员如何销毁, 析构部分是隐式的。成员销毁时发生什么完全依赖于成员的类型。销毁类类型的成员需要执行成员自己的析构函数。内置类型没有析构函数,因此销毁内置类型成员什么也不需要做。隐式销毁一个内置指针类型的成员不会 delete 它所指向的对象。

    与普通指针不同,智能指针是类类型,所以具有析构函数。与普通指针不同,智能指针成员在析构阶段会被自动销毁。

    3.2 调用析构函数

    无论何时一个对象被销毁,就会自动调用其析构函数:

    • 变量在离开其作用域时被销毁。
    • 当一个对象被销毁时,其成员被销毁。
    • 容器 (无论是标准库容器还是数组) 被销毁时,其元素被销毁。
    • 对于动态分配的对象,当对指向它的指针应用 delete 运算符时被销毁。
    • 对于临时对象,当创建它的完整表达式结束时被销毁。

    References

    (美) Stanley B. Lippman, (美) Josée Lajoie, (美) Barbara E. Moo 著, 王刚, 杨巨峰 译. C++ Primer 中文版[M]. 第 5 版. 电子工业出版社, 2013.
    https://www.informit.com/store/c-plus-plus-primer-9780321714114

    展开全文
  • C++vector容器-构造函数

    2021-01-30 00:28:41
    vector数据结构和数组非常相似,也称为单端数组,在数组的尾段可以做插入,删除操作...vector构造函数 功能描述: 创建vector容器 函数原型: 代码如下: #include <iostream> using namespace std; #include &l

    vector数据结构和数组非常相似,也称为单端数组,在数组的尾段可以做插入,删除操作

    vector不同于普通数组,vector可以动态扩展

    动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间

    vector容器结构:
    在这里插入图片描述

    vector容器的迭代器是支持随机访问的迭代器


    vector构造函数

    功能描述:
    创建vector容器

    函数原型:

    在这里插入图片描述

    代码如下:

    #include <iostream>
    using namespace std;
    #include <vector>
    //vector容构造
    
    void printVector(vector<int> &v) {
    	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
    		cout << *it << " ";
    	}
    	cout << endl;
    }
    
    
    void test01() {
    	vector<int>v1;//默认构造,无参构造
    
    	for (int i = 0; i < 10; i++) {
    		v1.push_back(i);
    	}
    	printVector(v1);
    	//通过区间方式进行构造
    	vector<int>v2(v1.begin(), v1.end());
    	printVector(v2);
    
    	//n个elem方式构造
    	vector<int>v3(10, 100);
    	printVector(v3);
    
    	//拷贝构造
    	vector<int >v4(v3);
    	printVector(v4);
    
    }
    
    
    
    int main() {
    	test01();
    
    	system("pause");
    	return 0;
    }
    
    展开全文
  • vector容器 功能:vector数据结构和数组非常相似,也称为单端数组 ...1.vector构造函数 创建vector容器 vector v; //采用模板实现类实现,默认构造函数 vector(v.begin(),v.end()); //将 v[begin(),end())
  • 直接上例子,更能清楚的显示区别: ...vector> using namespace std; struct X { X() { std::cout << "X()" << std::endl; } X(const X&) { std::cout << "X(const X&)" << std
  • vector容器的构造函数与赋值操作.
  • C++ 拷贝构造函数、移动构造函数、拷贝赋值函数、移动赋值函数 这是个类的代码,我用这个类来讲诉 C++ 拷贝构造函数、移动构造函数、拷贝赋值函数、移动赋值函数的使用。 例子: Speaker.h: #pragma once #ifndef _...
  • 1. 拷贝构造函数 如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。 class Foo{ public: Foo();//默认构造函数 (仅在程序员没有定义构造函数的情况下) ...
  • vector容器---构造函数

    2021-01-25 18:15:23
    1.vector基本概念 功能:vector数据结构和数组非常相似,也称为单端数组;...2.vector构造函数 函数原型: vector<T> v; //采用模板实现类实现,默认构造函数 vector(v.begin(),v.end()); //将v[ begin(),
  • //动态扩展:并不是在原空间之后接续新空间,而是找更大的空间,如何将原数据拷贝新空间,释放原空间 //push_back插入 pop_back弹出 //v.rend()第一个元素前一位 v.begin()第一个元素 //v.rbegin()最后一个元素前一位 v....
  • I have a class (Uniform) that has a constructor with 2 parameters, and a default copy constructor (it only contains int, floats, a std::vector and a std::map). I created astd::vector uniformsthat I wa...
  • std::vector的push_back与拷贝构造 移动构造的关系 #include <iostream> #include <vector> class T { public: T() = default; T(int32_t i) { m_indx = i; printf("constructor T\n"); } T...
  • C++的构造函数、拷贝构造函数、拷贝赋值函数 #include<iostream> #include<vector> #include<string> class Test { public: //构造函数 Test(std::string name = "",std::vector<double&...
  • StringBad ditto (motto); StringBad metoo = motto;...) - 其中中间两种声明可能会使用复制构造函数直接创建metoo和also对象,也可能使用复制构造函数生成一个临时对象,然后将临时对象的内容赋给me
  • 测试代码: #pragma once #include<iostream> class TempConstruct ..."调用默认构造函数" << std::endl; memset(nVar, 0, 4 * 10); } TempConstruct(const std::string& strTip) { st...
  • vector容器基本概念 功能: 与数组非常相似,也称为单端数组 ...并不是在原空间之后续借新空间,而是找到更大的内存空间,然后将原数据拷贝到新空间,释放原空间 vector容器的迭代器是支持随机 ...
  • iterator类型:iterator:到value_type的访问,value_type是模板的类型const_iterator:到const value_type的访问reverse_iterator:reverse_iteratorconst_reverse_iterator:reverse_iterator其中我们...构造函数:exp...
  • vector构造函数

    2021-11-01 20:39:34
    vector> using namespace std; void printvector(vector<int>&v) { for (vector<int>::iterator j = v.begin(); j != v.end(); j++) {//找到vector容器的迭代器 cout << *j << " ...
  • 类型不匹配,getchar() 函数返回值是int,只能赋值给int 或者char类型,此时p为char*类型 B scanf(“%s”,p); p指针在定义的时候没有分配内存, 所以这行代码在运行的时候会报野指针错误 C char s[...
  • 基本概念: 功能:vector数据结构和数组非常类似,也称为单端数组 vector与普通数组区别:数组是静态空间,...//vector构造函数 void print(vector<int>& v) { for (vector<int>::iterator beg = v.b
  • C++中复制函数在三种情况下自动调用: 用一个对象初始化另一个对象 函数的参数为对象 函数的返回值为对象 下面用几个代码片段解释复制函数的调用中的一些常见“坑”: 一:默认复制函数的自动调用 #include<...
  • vector成员函数实例详解(C++)

    千次阅读 多人点赞 2021-11-13 15:38:21
    目录前言1.vector进阶2.vector构造函数3.给vector容器进行赋值4.对vector容器的容量和大小操作5.对vector中的数据的存取操作6.利用函数swap()实现两个容器进行元素互换 前言 我们之前讲了有关vector容器的概述...
  • #include <...vector> using namespace std; class Only_number{ public: Only_number() = default; Only_number(const Only_number &n):number(n.number+1){} Only_number &operat.
  • 在c++11中,类是和5个特殊的函数紧密相关的,它们是析构函数、拷贝构造函数、移动构造函数、拷贝赋值运算符和移动赋值运算符。 析构函数 只要一个对象运行越出范围,或经受一次delete,则析构函数就要被调用。典型...
  • 可见性系列博客前言视频链接P41 C++运算符及其重载P42 C++的this关键字P43 C++的对象生存期(栈作用域生存期)P44 C++的智能指针P45 C++的复制与拷贝构造函数P46 C++的箭头操作符P47 C++的动态数组(std::vector)P...
  • java构造函数怎么写

    2021-03-14 22:56:30
    1.怎么写构造函数构造函数一般是用来进行初试化的,看你需要对哪些属性进行初始化#include#includeclass student{public:char name[10];float score;student(char[],float);//这里形式参数就是你要初试化的变量的...
  • 这个类中进行了自定义的动态内存管理,定义了析构函数,但未定义拷贝构造函数和拷贝赋值操作符,这违反了rule of three,rule of three: 析构函数 拷贝构造函数 拷贝赋值操作符 这三个函数应该一起提供或者一起不...
  • 构造函数和析构函数2.1 什么是构造函数2.2 构造函数和析构函数一般用法2.3 为什么需要构造函数和析构函数2.4 在构造和析构函数中使用动态内存 1.一个基础的C++面向对象编程 一般一个.cpp和一个.hpp文件配对来描述一...
  • 1、默认构造函数默认构造函数是指所有参数都提供了默认值的构造函数,通常指无参的构造函数或提供默认值的构造函数。如类Test1和Test2的构造函数classTest1{public:Test1(){}//default constructor} ;或classTest2{...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,272
精华内容 18,908
关键字:

vector拷贝构造函数