在QT里使用QList的时候,如果调用拷贝构造函数或拷贝赋值函数的时候,会报警告

image.png

image.png

原因

QList继承自QListSpecialMethods,QListSpecialMethods继承自QListSpecialMethodsBase,这两个类显示声明了析构函数,而QList里没有显示声明拷贝构造函数和拷贝赋值函数

image.png

image.png

image.png

在C++11及更高版本中,如果一个类定义了析构函数,编译器仍然会生成默认的拷贝构造函数和拷贝赋值函数,但是这种默认生成的拷贝函数是deprecated性质的,也就是说,它只是一种过渡性的实现,并不推荐使用。编译器认为自定义了析构函数,一般有资源,比如内存资源需要处理,拷贝函数中这些资源最好自定义处理,所以不显示定义拷贝函数,就会报警告。

如下例所示:

//test.h
#ifndef TEST
#define TEST

struct Base {
  protected:
    ~Base();
};

class Derived : public Base {
  public:
    Derived();
    ~Derived();

  public:
    int v = 10;
};

#endif  // TEST

//main.cpp
#include "test.h"

int main() {
    Derived d1;
    Derived d2 = d1;
    return 1;
}

c++11以上,编译器会给出警告,不过并不影响正常使用,未来可能新版本不再生成?

image.png

QList原理

  1. 通过函数调用返回QList

代码结构如下:

class Test {
  public:
    Test(int _v) : v(_v) {   cout << "constructor" << endl;
    }
    ~Test() = default;
    Test(const Test& test) {
         cout << "copy constructor" << endl;
        this->v = test.v;
    }
    Test& operator=(const Test& test) {
        if (this != &test) {
               cout << "copy assignment" << endl;
            this->v = test.v;
        }
        return *this;
    }
    int getV() const { return v; }
    void setV(int _v) const { const_cast<Test*>(this)->v = _v; }

  private:
    int v;
};

QList<Test> getList1() {
    QList<Test> c;
    for (int i = 0; i < COUNT; ++i) {
        c.push_back(Test(i));
    }

    c.at(0).setV(10);

    return c;
}

QList<Test> getList2() {
    QList<Test> c;
    for (int i = 0; i < COUNT; ++i) {
        c.push_back(Test(i));
    }

    c[0].setV(10);

    return c;
}

不管内部使用[]还是at,它操作的都是QList c,返回的时候使用QList的默认拷贝构造函数,仅仅是拷贝QList保存的对象的地址,所以不会有性能影响,相比std的容器,速度反而更快(相当于直接使用移动语义)

  1. 函数内赋值
class Test {
  public:
    Test(int _v) : v(_v) {   cout << "constructor" << endl;
    }
    ~Test() = default;
    Test(const Test& test) {
         cout << "copy constructor" << endl;
        this->v = test.v;
    }
    Test& operator=(const Test& test) {
        if (this != &test) {
               cout << "copy assignment" << endl;
            this->v = test.v;
        }
        return *this;
    }
    int getV() const { return v; }
    void setV(int _v) const { const_cast<Test*>(this)->v = _v; }

  private:
    int v;
};

void useAt() {
    QList<Test> c;
    for (int i = 0; i < COUNT; ++i) {
        c.push_back(Test(i));
    }

    QList<Test> d = c;

    c.at(0).setV(10);

    cout << &c.at(0) << "," << c.at(0).getV() << endl; //000001BD74F3E610,10
    cout << &d.at(0) << "," << d.at(0).getV() << endl; //000001BD74F3E610,10
}

void useBracket() {
    QList<Test> c;
    for (int i = 0; i < COUNT; ++i) {
        c.push_back(Test(i));
    }

    QList<Test> d = c;

    c[0].setV(10);

    cout << &c.at(0) << "," << c.at(0).getV() << endl; //000001BD74F3E670,10
    cout << &d.at(0) << "," << d.at(0).getV() << endl; //000001BD74F3DCB0,0
}

使用at进行强行改变的时候,两个QList的元素是共享一份内存数据,所以改变一个的时候,另一个也会被改变,使用[]的时候,QList会调用detach()函数,detach调用reallocateAndGrow,将内存数据拷贝一份(调用对象的拷贝构造函数),所以进行修改的时候,另一个不会被改变。

性能上如果使用[]的话,会慢于at(),毕竟会进行拷贝,所以如果只读的话,建议使用at,读写的话使用[].

at源码:

image.png

[]源码:

image.png

image.png

image.png


点墨
26 声望3 粉丝

全栈前端开发工程师