QT(18):QString

news/2024/7/21 3:48:03 标签: qt, 开发语言, c++, 算法, 图像处理

目录

    • QString
    • QTypedArrayData
    • QTypedArrayData
    • QLatin1String
      • QStringLiteral
      • 乱码
    • QStringRef

QString

QString 存储16位QChar的字符串,其中每个QChar对应一个 UTF-16代码单元。QString 使用(写入时复制copy-on-write)来减少内存使用并避免不必要的数据复制。这也有助于减少存储16位字符而不是8位字符的固有开销。

除了QString之外,Qt还提供了存储原始字节和传统8位“\0”结尾字符串的QByteArray类。在大多数情况下,使用QString,Unicode支持使得应用程序将很容易翻译。使用QByteArray类时合适的两种主要情况是需要存储原始二进制数据时,以及内存保护至关重要时(如在嵌入式系统中)。

typedef QStringData Data;
typedef QTypedArrayData<ushort> QStringData;
class Q_CORE_EXPORT QString
{
public:
	typedef QStringData Data;
	//构造函数、拷贝函数、赋值运算符...........
	//字符串属性函数、字符串操作函数,修改、比较、查找..........
private:
	Data *d;
};

QString内部除了定义的函数之外,成员变量只有Data类型的指针,指向字符数据地址。内部采用共享机制,在QString对象赋值时只是类似智能指针执行浅拷贝,简单复制指针d的值,增加引用计数。同时QString采用写时复制机制,只在修改数据时执行深拷贝,复制字符数据并进行写操作。

QString在构造时调用QTypedArray的allocate函数,将返回的地址赋值给指针d。以QChar为参数的构造函数,size设置为1,data返回地址开始存储字符数据,末尾存储结束字符。

QString::QString(QChar ch)
{
    d = Data::allocate(2);
    Q_CHECK_PTR(d);
    d->size = 1;
    d->data()[0] = ch.unicode();
    d->data()[1] = '\0';
}

以QString的const &为参数的拷贝构造函数只是复制指针,ref()增加计数值。

inline QString::QString(const QString &other) noexcept : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }

析构函数中,当引用计数为零时才会调用deallocate函数释放内存。

inline QString::~QString() { if (!d->ref.deref()) Data::deallocate(d); }

在使用left()、mid()等字符串操作时会修改字符串,此时会创建新的QString对象并在构造函数使用memcpy()进行深拷贝。

QString QString::left(int n)  const
{
    if (uint(n) >= uint(d->size))
        return *this;
    return QString((const QChar*) d->data(), n);
}

QTypedArrayData

QString的数据存放在指针d指向的对象中,对象类型为QTypedArrayData,继承自QArrayData。
QTypedArrayData重写的data()、allocate()、reallocateUnaligned()、deallocate()、sharedNull()都是直接调用了父结构体的函数,此外还基于data()封装了一些迭代器操作。

在QString中,类模板QTypedArrayData的模板实参T为ushort,所以是以两字节的方式存储字符数据的。data函数的返回值为ushort类型的指针,后续在QString中通过data()返回指针的[]运算符函数对字符数据进行操作。

template <class T>
struct QTypedArrayData
    : QArrayData
{
	typedef T* iterator;
    typedef const T* const_iterator;
    T *data() { return static_cast<T *>(QArrayData::data()); }
    const T *data() const { return static_cast<const T *>(QArrayData::data()); }

    iterator begin(iterator = iterator()) { return data(); }
    iterator end(iterator = iterator()) { return data() + size; }
    const_iterator begin(const_iterator = const_iterator()) const { return data(); }
    const_iterator end(const_iterator = const_iterator()) const { return data() + size; }
    const_iterator constBegin(const_iterator = const_iterator()) const { return data(); }
    const_iterator constEnd(const_iterator = const_iterator()) const { return data() + size; }

    class AlignmentDummy { QArrayData header; T data; };

    Q_REQUIRED_RESULT static QTypedArrayData *allocate(size_t capacity,
            AllocationOptions options = Default)
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T),
                    Q_ALIGNOF(AlignmentDummy), capacity, options));
    }

    static QTypedArrayData *reallocateUnaligned(QTypedArrayData *data, size_t capacity,
            AllocationOptions options = Default)
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        return static_cast<QTypedArrayData *>(QArrayData::reallocateUnaligned(data, sizeof(T),
                    capacity, options));
    }

    static void deallocate(QArrayData *data)
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy));
    }

    static QTypedArrayData *fromRawData(const T *data, size_t n,
            AllocationOptions options = Default) {  //.............}

    static QTypedArrayData *sharedNull() noexcept
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        return static_cast<QTypedArrayData *>(QArrayData::sharedNull());
    }

    static QTypedArrayData *sharedEmpty()
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        return allocate(/* capacity */ 0);
    }

#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
    static QTypedArrayData *unsharableEmpty()
    {
        Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
        return allocate(/* capacity */ 0, Unsharable);
    }
#endif
};

QTypedArrayData

QTypedArrayData重写的data()、allocate()、reallocateUnaligned()、deallocate()、sharedNull()都是直接调用了父结构体QArrayData的函数,所以字符数据和基础的操作都定义在QArrayData中,QArrayData即数据容器。

[ref|size|alloc|reserve|offset|--the real data--]
struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;

    qptrdiff offset; //从标头开头开始的字节数
    
    void *data()
    {
        Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData));
        return reinterpret_cast<char *>(this) + offset;
    }

    const void *data() const {//.......}
	//这是指数组数据的可变性,而不是 QArrayData 中数据成员表示的“标头数据”。
	//共享数据(数组和标头)仍必须遵循 COW 原则。
    bool isMutable() const { return alloc != 0; }

    enum AllocationOption {//内存分配方式
        CapacityReserved    = 0x1,//调整内存大小或克隆时至少保留原始容量大小。
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
        Unsharable          = 0x2,//非共享的,在克隆时必须重新分配内存
#endif
        RawData             = 0x4,//如果设置了RawData,分配内存时就会忽略填充而使数据紧贴在QArrayData头之后。
        Grow                = 0x8,//会分配超出用户要求的内存,达到比用户要求数量更大的下一个2次幂

        Default = 0//
    };

    Q_DECLARE_FLAGS(AllocationOptions, AllocationOption)
    size_t detachCapacity(size_t newSize) const {//................}
    AllocationOptions detachFlags() const {//................}
    AllocationOptions cloneFlags() const {//................}

    Q_REQUIRED_RESULT static QArrayData *allocate(size_t objectSize, size_t alignment,
            size_t capacity, AllocationOptions options = Default) noexcept;
    Q_REQUIRED_RESULT static QArrayData *reallocateUnaligned(QArrayData *data, size_t objectSize,
            size_t newCapacity, AllocationOptions newOptions = Default) noexcept;
    static void deallocate(QArrayData *data, size_t objectSize,
            size_t alignment) noexcept;

    static const QArrayData shared_null[2];
    static QArrayData *sharedNull() noexcept { return const_cast<QArrayData*>(shared_null); }
};

通过QChar或者char实例化一个QString对象时调用allocate函数,内部调用了malloc分配内存,并将QArrayData指针对象header指向该内存,初始化标头数据,并返回header赋值给指针d。

QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
        size_t capacity, AllocationOptions options) noexcept
{
    //..............
    QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize));
    if (header) {
        quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
                & ~(alignment - 1);

#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
        header->ref.atomic.storeRelaxed(bool(!(options & Unsharable)));
#else
        header->ref.atomic.storeRelaxed(1);
#endif
        header->size = 0;
        header->alloc = capacity;
        header->capacityReserved = bool(options & CapacityReserved);
        header->offset = data - quintptr(header);
    }
    return header;
}

QLatin1String

如果没有定义QT_NO_CAST_FROM_ASCIIQT_RESTRICTED_CAST_FROM_ASCII两个宏,在QString中会定义一系列以const char*为参数的函数,比如拷贝构造函数和拷贝赋值运算符,支持QString和字符串常量的转换和比较。而如果定义了该宏,类似 QString dd = "example"dd == "example" 的操作是不能通过编译的,因为相关函数将失效,并且定义QT_NO_CAST_FROM_ASCII后将几个函数定义成私有的。
Qt提供了QLatin1String类来更高效的利用const char*的类型,dd == "example"转换成dd == QLatin1String("example")
虽然在代码输入的时候有点长,但是两者效率差不多,同时也比使用QString::fromLatin1()转换更快。

class QLatin1String
{
public:
    Q_DECL_CONSTEXPR inline QLatin1String() noexcept : m_size(0), m_data(nullptr) {}
    Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s) noexcept : m_size(s ? int(strlen(s)) : 0), m_data(s) {}
    Q_DECL_CONSTEXPR explicit QLatin1String(const char *f, const char *l)
        : QLatin1String(f, int(l - f)) {}
    Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s, int sz) noexcept : m_size(sz), m_data(s) {}
    inline explicit QLatin1String(const QByteArray &s) noexcept : m_size(int(qstrnlen(s.constData(), s.size()))), m_data(s.constData()) {}
    
    Q_DECL_CONSTEXPR const char *latin1() const noexcept { return m_data; }
    Q_DECL_CONSTEXPR int size() const noexcept { return m_size; }
    Q_DECL_CONSTEXPR const char *data() const noexcept { return m_data; }

	//字符串操作
    //类型别名
private:
    int m_size;
    const char *m_data;
};

QStringLiteral

在编译时构造QString对象,适用于只能接受QString参数的场景,转换后的字符串数据存储在编译后文件的只读数据段中,使用时读取。接受const char *或QLatin1String直接使用比该宏更高效。

乱码

const char *字符默认UTF-8编码,QString采用UTF-16编码。根据源文件相应格式解码后,Windows环境下可执行文件中的字符串是本地编码格式GBK编码,运行时以UTF-8解码,再进行UTF-16编码就会出现乱码。
解决方法:
1、解码:QStringLiteral()宏或者QString::fromLocal8Bit()封装字符串
2、编码:编译器采用UTF-8编码生成可执行文件

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

QStringRef

QStringRef 提供 API 的只读子集。此类旨在提高操作从现有实例获取的子字符串时子字符串处理的性能。QStringRef 通过简单地引用原始字符串的一部分来避免标准的内存分配和引用计数开销(修改字符串操作如mid、left、right等,会创建一个新的字符串,申请空间并拷贝数据)。这在低级代码(例如解析器中使用的代码)中可能被证明是有利的,但代价是可能更复杂的代码。

对于大多数用户来说,使用 QStringRef 而不是 QStringRef 没有语义上的好处,因为 QStringRef 需要注意内存管理问题,这可能会使代码的编写和维护更加复杂。

class Q_CORE_EXPORT QStringRef {
    const QString *m_string;
    int m_position;
    int m_size;
public:
    inline QStringRef(const QString *string, int position, int size);
    //............
};

http://www.niftyadmin.cn/n/5218980.html

相关文章

【深入剖析K8s】容器技术基础(三):深入理解容器镜像 文件角度

容器里的进程‘看到’’的文件系统 可能你立刻就能想到,这应该是一个关于MountNamespace的问题:容器里的应用进程理应‘看到”一套完全独立的文件系统这样它就可以在自己的容器目录&#xff08;比如 /tmp&#xff09;下进行操作’而完全不会受宿主机以及其他容器的影响。 容器…

在UE中使用C++时的Pascal命名法

Pascal命名法的规则 Pascal命名法的基本原则是&#xff1a;所有标识符以大写字母开头&#xff0c;且标识符中只能由字母、数字和下划线组成&#xff0c;但是数字和下划线都不能放在开头。 Pascal命名法有两种形式&#xff0c;一种是大驼峰命名法&#xff0c;另一种是全部大写加…

百度人工智能培训第二天笔记

参加了百度人工智能初步培训&#xff0c;主要是了解一下现在人工智能的基本情况&#xff0c;以便后续看可以参与一些啥&#xff1f; 下面就继续前一天的内容记录。 一、先做电动自行车的电梯里检测 先进行图片资料的上传与标注&#xff0c;这个昨天的最好也说了一下。 训练完后…

【开题报告】基于uniapp的瑜伽学习交流小程序的设计与实现

1.选题背景 瑜伽在现代社会中越来越受到人们的关注和喜爱。它不仅可以帮助人们塑造健美的身材&#xff0c;还能促进身心健康、提高生活质量。然而&#xff0c;由于瑜伽动作的复杂性和技巧性&#xff0c;很多初学者在学习过程中会遇到困难和挑战。 同时&#xff0c;由于工作和…

2027年,人工智能(AI)用电量或相当于荷兰一年用电量 |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 2027年&#xff0c;人工智能&#xff08;AI&#xff09;用电量或相当于荷兰一…

【面经八股】搜广推方向:面试记录(三)

【面经&八股】搜广推方向:面试记录(三) 文章目录 【面经&八股】搜广推方向:面试记录(三)1. 编程题1.1 大数乘法1.2 大数加法2. 项目介绍3. 有了解过的广告推荐模型吗4. 广告模型回归问题1. 编程题 上来直接写编程题,有点儿懵逼。 1.1 大数乘法 可以参考 该博…

【Spring】Spring是什么?

文章目录 前言什么是Spring什么是容器什么是 IoC传统程序开发控制反转式程序开发理解Spring IoCDI Spring帮助网站 前言 前面我们学习了 servlet 的相关知识&#xff0c;但是呢&#xff1f;使用 servlet 进行网站的开发步骤还是比较麻烦的&#xff0c;而我们本身程序员就属于是…

laravel 重写批量添加,自动维护时间戳

laravel 自带的批量添加是不会自动维护时间戳的&#xff0c;意思是说&#xff0c;使用laravel的批量添加&#xff0c;时间戳字段不会插入&#xff0c;也不会在更新的时候进行更新。 使用继承或者trait来解决这个问题&#xff0c;在这里感谢Mr.wen <?php namespace App\Mo…