C++11(2)

news2024/11/24 14:26:22

目录

6。左值引用和右值引用

下面演示左值和左值引用:

下面演示右值和右值引用:

7。移动构造和移动赋值

VS2022的神级优化

List下的移动构造拷贝和深拷贝(实战)

证明:左值是由编译器决定的

附加:自己实现的List.h


6。左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们 之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。

我们先看看左值和右值的定义和如何使用吧!!!

//左值和右值
//都是给对象起别名
//右值也是有地址的,只是在语法层取不了

下面演示左值和左值引用:

int main()
{
    // 以下的p、b、c、*p都是左值
    // 左值:一般情况下可以取地址+可以对其赋值+左值可以出现在赋值符号的左边
    //const修饰的左值不可以修改所以不能对其赋值

    int* p = new int(0);
    int b = 1;
    const int c = b;
    *p = 10;
    string s1 = { "ddddd" };
    //s1[0];
    // 左值引用给左值取别名

    int& h = b;
    string& s2 = s1;
    int*& p1 = p;
    return 0;
}

下面演示右值和右值引用:

int main()
{
    double x = 1.1;
    double y = 2.2;
    //常见的右值可以是字面常量(比如10这种),函数的返回值(不能是左值引用返回),表达式返回值(临时变量,匿名对象)
    //x + y
    //10
    //fmin(x,y)
    //string("11111")---表达式的返回值
    //右值只能出现在表达式的右边,且不可以取地址的,也不可以修改的
    //右值引用
    //double& p = x + y;//不能直接左值引用的
    //string& s1 = string("wefewfwfe");//同上
    //右值引用给右值取别名, 下面演示正确的右值引用方法

    int&& p = 10;
    double&& h = x + y;
    string&& s1 = string("hdehveb");
    string&& s1 = { "cdfw" };//隐式类型转换会形成临时对象,得用右值
    //鉴于右值这种特性又可以分为,纯右值---内置类型右值和将亡值---类类型右值
    return 0;
}

int main()
{
    int x = 10;
    int& ret1 = x;

    int&& ret2 = x + 10;
    //进入汇编层了之和我们会发现无论是左值还是右值底层都差不多
    //这边底层和语法层是分裂的,在底层中右值也是有地址的,只是语法层取不了而已
    //左值和右值完全由编译器决定的,没有哪个值是天生的左值或者右值


    
    //左值引用引用给右值取别名:不能直接引用,但是const 左值引用可以
    //原本不能的原因是权限被放大了,加上const加以缩小
    const int& ret3 = x + 10;
    //右值引用引用给左值取别名:不能直接引用,但是move(左值)以后右值引用可以引用
    //所以move的作用就是将原本是左值的值变成右值

    int&& p = move(x);
    string s1 = { "cdcc" };
    string&& s2 = move(s1);
    vector<string> s4;
    s4.push_back(s1);
    s4.push_back(string());
    s4.push_back(string("dcdfwefe"));
    //以上都能成功插入s4,说明C++11中的容器的push_back能同时插入左值和右值了
    return 0;
}

// 底层汇编等实现和上层语法表达的意义,有时是背离的,所以不要结合到一起去理解,互相佐证

7。移动构造和移动赋值

前面我们可以看到左值引用既可以引用左值和又可以引用右值,那为什么C++11还要提出右值引 用呢?是不是化蛇添足呢?下面我们来看看左值引用的短板,右值引用是如何补齐这个短板的!

#include<iostream>
#include<assert.h>//要加.h
using namespace std;
// 引用的意义:减少拷贝
// 左值引用解决的场景:引用传参/引用传返回值
// 左值引用没有彻底解决的场景:传返回值

namespace bit
{
    class string
    {
    public:
        typedef char* iterator;
        iterator begin()
        {
            return _str;
        }
        iterator end()
        {
            return _str + _size;
        }

        typedef const char* const_iterator;
        const_iterator begin() const
        {
            return _str;
        }

        const_iterator end() const
        {
            return _str + _size;
        }

        string(const char* str = "")
            :_size(strlen(str))
            , _capacity(_size)
        {
            cout << "string(char* str)" << endl;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        // s1.swap(s2)
        void swap(string& s)
        {
            ::swap(_str, s._str);
            ::swap(_size, s._size);
            ::swap(_capacity, s._capacity);
        }

        // 拷贝构造
        // s2(s1)

        string(const string& s)
            :_str(nullptr)
        {
            cout << "string(const string& s) -- 深拷贝" << endl;

            reserve(s._capacity);
            for (auto ch : s)
            {
                push_back(ch);
            }
        }

        // 移动构造
        // 临时创建的对象,不能取地址,用完就要消亡
        // 深拷贝的类,移动构造才有意义

        string(string&& s)
        {
            cout << "string(string&& s) -- 移动拷贝" << endl;
            swap(s);//反正右值也就是将亡值了,出了作用域也就消亡了
                    //直接取代它的空间就行了,不需要额外创造空间再拷贝过去
        }

        // 赋值重载
        string& operator=(const string& s)
        {
            cout << "string& operator=(const string& s) -- 深拷贝" << endl;
            if (this != &s)
            {
                _str[0] = '\0';
                _size = 0;

                reserve(s._capacity);
                for (auto ch : s)
                {
                    push_back(ch);
                }
            }

            return *this;
        }

        // 移动赋值
        string& operator=(string&& s)
        {
            cout << "string& operator=(string&& s) -- 移动拷贝" << endl;

            swap(s);
            return *this;
        }

        ~string()
        {
            delete[] _str;
            _str = nullptr;
        }

        char& operator[](size_t pos)
        {
            assert(pos < _size);
            return _str[pos];
        }

        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char* tmp = new char[n + 1];
                if (_str)
                {
                    strcpy(tmp, _str);
                    delete[] _str;
                }
                _str = tmp;
                _capacity = n;
            }
        }

        void push_back(char ch)
        {
            if (_size >= _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }

            _str[_size] = ch;
            ++_size;
            _str[_size] = '\0';
        }

        //string operator+=(char ch)
        string& operator+=(char ch)
        {
            push_back(ch);
            return *this;
        }

        const char* c_str() const
        {
            return _str;
        }
    private:
        char* _str = nullptr;
        size_t _size = 0;
        size_t _capacity = 0; // 不包含最后做标识的\0
    };

    bit::string to_string(int value)
    {
        bool flag = true;
        if (value < 0)
        {
            flag = false;
            value = 0 - value;
        }
        bit::string str;
        cout << &str << endl;//打印str的地址
        while (value > 0)
        {
            int x = value % 10;
            value /= 10;
            str += ('0' + x);
        }

        if (flag == false)
        {
            str += '-';
        }

        std::reverse(str.begin(), str.end());

        return str;
    }
}

#include"List.h"
int main()
{
    //C++11之后对于容器新加入了移动构造和移动赋值
    bit::string s1 = bit::to_string(1234);//连拷贝都没有了,s1相当于str的引用,原理上相当于只构造了s1
    cout << &s1 << endl;//分别打印s1和str的地址会发现两个地址都是一样的
    return 0;
}

以上代码为一个自定义的string的自我实现,代码很容易理解,加入了移动拷贝和移动赋值,其实C++11之后很多容器的很多函数都加入了移动拷贝和移动赋值。

不对呀,为什么没有拷贝呢, 这个赋值bit::string s1 = bit::to_string(1234);按照C++原本的语法来说应该是先在函数体里面构造str,然后传值返回会生成临时对象会生成一层构造,然后这个临时对象再赋值给s1理论上又会生成一次拷贝,所以至少也有两次构造,为什么这里只有一次构造和没有拷贝呢?对,我这边推理的完全没有问题,非常的C++,但是你没有发现这样很麻烦吗。所以编译器一般这样优化:

不生成str在返回时不临时对象了,直接调用移动拷贝,拷贝赋值给s1,这样就只有一下构造+一次拷贝了。但是也不对呀,这里是完全没有拷贝的呀。

这里就不得不提一下VS2022的神级优化!!!

VS2022的神级优化

VS2022的优化是这么说的:如果函数内部构造一个对象,这个对象作为返回值,并且上一个函数栈中直接进行接收了,就直接构造上一个函数栈中的对象。也就是说s1作为最初的栈帧,接受到了来自上一层栈帧的str的返回值,就直接那str构造s1,就相当于str是s1的别名,两个地址一样也说明了这一点。

简单来说就是这个神级优化让str直接构造s1,相当于连原本的移动拷贝都没有了。但是有的同学通过调试发现不对呀,当跳到bit::string str这一行之后就跳到构造函数了,所以我感觉是直接构造str,s1类似像str的左值引用一样,这里是你理解错了,要完全理解为什么是构造s1需要进一步查看底层(编码),这个是编译器把实际的代码做了修改,虽然看着是构造str,实际上构造的是s1。

但是这个优化对比正常的编译器至少将移动拷贝优化掉了,但是我们知道移动构造是针对右值的,也就是将亡值的,本身消耗就很小了,所以这个神级优化感觉没有什么实际意义。

上面可以看到很多容器都加入了移动构造和移动拷贝和移动赋值,像日期类这种类是没有移动拷贝和移动构造的必要的,因为里面的数据走深拷贝的代价不大,所以这更体现了这个神级优化的作用其实不大。

其实这个神级优化不是每种情景都起作用的:比如下面这种:

int main()
{
    //C++11之后对于容器新加入了移动构造和移动赋值
    bit::string s1;
    s1 = bit::to_string(1234);//只能先构造s1,然后,构造str,然后
                                //由于str是右值是将亡值所以直接调用了移动构造,构造了s1
    return 0;
}

List下的移动构造拷贝和深拷贝(实战)

#include"List.h"

int main()
{
    bit::list<bit::string> lt;
    bit::string s1("111111111111111111111");
    lt.push_back(s1);//左值
    
    lt.push_back(bit::string("22222222222222222222222222222"));//右值
    
    lt.push_back("3333333333333333333333333333");//右值
    
    lt.push_back(move(s1));//右值
    
    bit::string&& r1 = bit::string("22222222222222222222222222222");
    //r1(右值引用本身)的属性是左值,引用了右值的值是左值
    return 0;
}

List.h部分在下面会提供出来,我们先看一下由于左值调用的肯定是深拷贝,后面push_back的三个肯定是右值调用的是移动构造,没有问题,最上面两行由于要先构造哨兵位结点再拷贝,所以前面两行可以不用看,最后这个是神级优化。

然后如果要调用移动拷贝就需要在原本的基础上重载一个能传入右值的push_back

由于push_back调用的是insert,所以:

接着由于insert里面创造了一个新的结点会调用构造函数,所以构造函数也需要一个右值版本:

最后由于在List的构造函数里面_data会调用对应类型的构造函数,string会调用string的,为了最后变成移动构造,所以也需要将传进去的值变成右值。

传入上一个函数,完成浅拷贝,我们会发现这一路上只有所有函数都接收的是右值才会最后变成移动拷贝呀,又由于引用右值的值如果不做特殊处理是会被编译器默认为左值的(这个很容易证明,其实是由于引用右值的那个值是可以修改的,但是右值是不可以修改的,所以是左值),所以移动拷贝的那条路径上的所有函数的接收的左值都要强制性转换成右值,我们先都使用move一一进行修改,后面为了方便可以使用完美转发——万能引用,否则只有一个地方没有修改(传的是左值),就会走到深拷贝那里!!!

证明:左右值是由编译器决定的

我们可以使用以下程序证明:

void func(const bit::string& s)
{
    cout << "void func(bit::string& s)" << endl;
}

void func(bit::string&& s)
{
    cout << "void func(bit::string&& s)" << endl;
}


int main()
{
    bit::string s1("1111111");
    func(s1);
//左值

    func((bit::string&&)s1);//强制性转换成右值


    func(bit::string("1111111"));//右值
    func((bit::string&)bit::string("1111111"));//强制性转换成左值

    return 0;
}

我们可以看到左右值是可以通过编译器互相强制性类型转换的,所有可以推得他们就是同一种类型的东西,没有哪个值天生就是左值或者右值。

附加:自己实现的List.h

//#include<iostream>
#include<assert.h>
//using namespace std;
namespace bit
{
    template<class T>
    struct ListNode
    {
        ListNode<T>* _next;
        ListNode<T>* _prev;

        T _data;

        ListNode(const T& data = T())
            :_next(nullptr)
            , _prev(nullptr)
            , _data(data)
        {}

        ListNode(T&& data)
            :_next(nullptr)
            , _prev(nullptr)
            , _data(move(data))
        {}
    };

    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T, Ref, Ptr> Self;
        Node* _node;

        ListIterator(Node* node)
            :_node(node)
        {}

        // ++it;
        Self& operator++()
        {
            _node = _node->_next;
            return *this;
        }

        Self& operator--()
        {
            _node = _node->_prev;
            return *this;
        }

        Self operator++(int)
        {
            Self tmp(*this);
            _node = _node->_next;

            return tmp;
        }

        Self& operator--(int)
        {
            Self tmp(*this);
            _node = _node->_prev;

            return tmp;
        }

        Ref operator*()
        {
            return _node->_data;
        }

        Ptr operator->()
        {
            return &_node->_data;
        }

        bool operator!=(const Self& it)
        {
            return _node != it._node;
        }

        bool operator==(const Self& it)
        {
            return _node == it._node;
        }
    };

    //template<class T>
    //class ListConstIterator
    //{
    //    typedef ListNode<T> Node;
    //    typedef ListConstIterator<T> Self;

    //    Node* _node;
    //public:
    //    ListConstIterator(Node* node)
    //        :_node(node)
    //    {}

    //    // ++it;
    //    Self& operator++()
    //    {
    //        _node = _node->_next;
    //        return *this;
    //    }

    //    Self& operator--()
    //    {
    //        _node = _node->_prev;
    //        return *this;
    //    }

    //    Self operator++(int)
    //    {
    //        Self tmp(*this);
    //        _node = _node->_next;

    //        return tmp;
    //    }

    //    Self& operator--(int)
    //    {
    //        Self tmp(*this);
    //        _node = _node->_prev;

    //        return tmp;
    //    }

    //    //*it
    //    const T& operator*()
    //    {
    //        return _node->_data;
    //    }

    //    const T* operator->()
    //    {
    //        return &_node->_data;
    //    }

    //    bool operator!=(const Self& it)
    //    {
    //        return _node != it._node;
    //    }

    //    bool operator==(const Self& it)
    //    {
    //        return _node == it._node;
    //    }
    //};

    template<class T>
    class list
    {
        typedef ListNode<T> Node;
    public:
      
 // 不符合迭代器的行为,无法遍历
        //typedef Node* iterator;
        //typedef ListIterator<T> iterator;
        //typedef ListConstIterator<T> const_iterator;

        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T*> const_iterator;

        iterator begin()
        {
            
//iterator it(_head->_next);
            //return it;

            return iterator(_head->_next);
        }

        const_iterator begin() const
        {
            return const_iterator(_head->_next);
        }

        iterator end()
        {
            return iterator(_head);
        }

        const_iterator end() const
        {
            return const_iterator(_head);
        }

        void empty_init()
        {
            _head = new Node();
            _head->_next = _head;
            _head->_prev = _head;
        }

        list()
        {
            empty_init();
        }

        list(initializer_list<T> il)
        {
            empty_init();

            for (const auto& e : il)
            {
                push_back(e);
            }
        }

        // lt2(lt1)
        list(const list<T>& lt)
        {
            empty_init();

            for (const auto& e : lt)
            {
                push_back(e);
            }
        }

        // lt1 = lt3
        list<T>& operator=(list<T> lt)
        {
            swap(_head, lt._head);

            return *this;
        }

        ~list()
        {
            clear();
            delete _head;
            _head = nullptr;
        }

        void clear()
        {
            auto it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }

        void push_back(const T& x)
        {
            insert(end(), x);
        }

        void push_back(T&& x)
        {
            insert(end(), move(x));
        }

        void pop_back()
        {
            erase(--end());
        }

        void push_front(const T& x)
        {
            insert(begin(), x);
        }

        void pop_front()
        {
            erase(begin());
        }

        // 没有iterator失效
        iterator insert(iterator pos, const T& x)
        {
            Node* cur = pos._node;
            Node* newnode = new Node(x);
            Node* prev = cur->_prev;

            // prev  newnode  cur
            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;

            return iterator(newnode);
        }

        iterator insert(iterator pos, T&& x)
        {
            Node* cur = pos._node;
            Node* newnode = new Node(move(x));
            Node* prev = cur->_prev;

            // prev  newnode  cur
            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;

            return iterator(newnode);
        }

        // erase 后 pos失效了,pos指向节点被释放了
        iterator erase(iterator pos)
        {
            assert(pos != end());

            Node* cur = pos._node;
            Node* prev = cur->_prev;
            Node* next = cur->_next;

            prev->_next = next;
            next->_prev = prev;

            delete cur;

            return iterator(next);
        }

    private:
        Node* _head;
    };

    void Func(const list<int>& lt)
    {
      
 // const iterator const 迭代器不能普通迭代器前面加const修饰
        // const 迭代器目标本身可以修改,指向的内容不能修改 类似const T* p 

        list<int>::const_iterator it = lt.begin();
        while (it != lt.end())
        {
          
 // 指向的内容不能修改
            //*it += 10;

            cout << *it << " ";
            ++it;
        }
        cout << endl;
    }

    void test_list1()
    {
        list<int> lt1;

        // 按需实例化(不调用就不实例化这个成员函数)
        lt1.push_back(1);
        lt1.push_back(2);
        lt1.push_back(3);
        lt1.push_back(4);
        lt1.push_back(5);

        Func(lt1);

        //ListIterator<int> it = lt1.begin();
        list<int>::iterator it = lt1.begin();
        while (it != lt1.end())
        {
            *it += 10;

            cout << *it << " ";
            ++it;
        }
        cout << endl;

        for (auto e : lt1)
        {
            cout << e << " ";
        }
        cout << endl;
    }

    struct Pos
    {
        int _row;
        int _col;

        Pos(int row = 0, int col = 0)
            :_row(row)
            , _col(col)
        {}
    };

    void test_list2()
    {
        list<Pos> lt1;
        lt1.push_back(Pos(100, 100));
        lt1.push_back(Pos(200, 200));
        lt1.push_back(Pos(300, 300));

        list<Pos>::iterator it = lt1.begin();
        while (it != lt1.end())
        {
          
 //cout << (*it)._row << ":" << (*it)._col << endl;
            // 为了可读性,省略了一个->

            cout << it->_row << ":" << it->_col << endl;
          
 //cout << it->->_row << ":" << it->->_col << endl;
            cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;

            ++it;
        }
        cout << endl;
    }


    void test_list4()
    {
        list<int> lt1;
        lt1.push_back(1);
        lt1.push_back(2);
        lt1.push_back(3);
        lt1.push_back(4);
        lt1.push_back(5);

        Func(lt1);

        lt1.push_front(10);
        lt1.push_front(20);
        lt1.push_front(30);

        Func(lt1);

        lt1.pop_front();
        lt1.pop_front();
        Func(lt1);

        lt1.pop_back();
        lt1.pop_back();
        Func(lt1);

        lt1.pop_back();
        lt1.pop_back();
        lt1.pop_back();
        lt1.pop_back();
        
//lt1.pop_back();
        Func(lt1);
    }

    void test_list5()
    {
        list<int> lt1;
        lt1.push_back(1);
        lt1.push_back(2);
        lt1.push_back(3);
        lt1.push_back(4);
        lt1.push_back(5);
        Func(lt1);

        list<int> lt2(lt1);

        lt1.push_back(6);

        Func(lt1);
        Func(lt2);

        list<int> lt3;
        lt3.push_back(10);
        lt3.push_back(20);
        lt3.push_back(30);

        lt1 = lt3;
        Func(lt1);
        Func(lt3);
    }

    void test_list6()
    {
        list<int> lt1 = { 1,2,3,4,5,6 };
        Func(lt1);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2123958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欺诈文本分类检测(十四):GPTQ量化模型

1. 引言 量化的本质&#xff1a;通过将模型参数从高精度&#xff08;例如32位&#xff09;降低到低精度&#xff08;例如8位&#xff09;&#xff0c;来缩小模型体积。 本文将采用一种训练后量化方法GPTQ&#xff0c;对前文已经训练并合并过的模型文件进行量化&#xff0c;通…

fuxa搭建与使用(web组态)

1. 安装Node.js -> npm安装 参考网址&#xff1a;https://blog.csdn.net/WHF__/article/details/129362462 一、安装运行 C:\WINDOWS\system32>node -v v20.17.0 C:\WINDOWS\system32>npm -v 10.8.2 二、环境配置 在安装路径&#xff08;D:\Program_Files\nodejs&a…

打开VSCod安装“PHP Intelephense”或“PHP Server”PHP扩展

安装PHP扩展&#xff1a; 打开VSCode&#xff0c;进入扩展视图&#xff08;或使用快捷键CtrlShiftX&#xff09;。搜索并安装“PHP Intelephense”或“PHP Server”等PHP相关扩展&#xff0c;这些扩展提供了PHP代码的智能感知、代码导航、调试等功能。 配置PHP路径&#xff1a;…

第2章 方法

本书作者起初以为仅靠研究命令行工具和指标就能提高性能。他认为这样不对。他从头到尾读了一遍手册&#xff0c;看懂了缺页故障、上下文切换和其他各种系统指标的定义&#xff0c;但不知道如何处理它们&#xff1a;如何从发现信号到找到解决方案。 他注意到&#xff0c;每当出…

第 8 章图像内容分类

本章介绍图像分类和图像内容分类算法。首先&#xff0c;我们介绍一些简单而有效的方法和目前一些性能最好的分类器&#xff0c;并运用它们解决两类和多类分类问题&#xff0c;最后展示两个用于手势识别和目标识别的应用实例。 8.1 K邻近分类法&#xff08;KNN&#xff09; 在…

2024最新精选文章!分享5款论文ai生成软件

在2024年&#xff0c;AI论文生成软件的出现极大地提升了学术写作的效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。以下是五款值得推荐的AI论文生成软件&#xff0c;其中特别推荐千笔-AIPassPaper。 ### 千笔-AIPa…

shader 案例学习笔记之step函数

step函数 step(edge,x)&#xff1a;当x>edge时返回1&#xff0c;否则返回0 #ifdef GL_ES precision mediump float; #endifuniform vec2 u_resolution;void main(){vec2 st gl_FragCoord.xy/u_resolution.xy;float f step(0.5, st.x);gl_FragColor vec4(f, 0, 0, 1.0); …

JQuery中的$

}); 当jquery.color.js动画插件加载完毕后&#xff0c;单击id为“go”按钮时&#xff0c;class为block的元素就有了颜色动画变化。 $.getJSON() $.getJSON()方法用于加载JSON文件&#xff0c;与$.getScript()方法的用法相同。 我们来看demo2. demo2.html 已有评论&#xff1…

Linux下如何使用CMake实现动态库的封装与调用

一、动态库的封装 1.创建工程 首先创建一个qt工程&#xff08;这里我采用的是ui工程&#xff09; 这里选择Widget工程 名字取一个和动态库相关的即可&#xff0c;我这里取的UIDLL 这里选择CMake 这里我选择命名为Dynamic kits采用Qt 5.14.2 GCC 64bit&#xff0c;之后直接下一…

Qt常用控件——QLabel

文章目录 QLabel核心属性文本格式演示显示图片文本对齐自动换行、边距、缩进设置伙伴 QLabel核心属性 QLabel是显示类控件&#xff0c;可以用来显示文本和图片 属性说明textQLabel中的文本textFormat文本格式Qt::PlainText纯文本Qt::RichText富文本&#xff08;支持html标签&…

html+css网页设计 旅游 雪花旅行社5个页面

htmlcss网页设计 旅游 雪花旅行社5个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

Windows下meson编译libplacebo库

1、安装msys64&#xff0c;添加系统环境变量C:\msys64 2、修改msys64目录下msys2_shell.cmd文件&#xff1a; 去掉“rem set MSYS2_PATH_TYPEinherit” 中rem&#xff0c;以便于shell能继承系统环境变量&#xff0c;然后保存 rem To export full current PATH from environme…

ESP32 TCP通信交换数据Mixly Arduino编程

TCP通信交换数据 在ESP32与ESP32或其它局域网络内主机间传输数据时&#xff0c;TCP是很方便的&#xff0c;特别当我们连接互联网后ESPnow不能用&#xff0c;MQTT又不稳定发送大量的数据&#xff0c;同时蓝牙有其它用途时&#xff0c;那么学会TCP通信协议就变得十分重要。 一、…

Python数据结构集合的相关介绍

集合是一种无序、可变的数据结构&#xff0c;它也是一种变量类型&#xff0c;集合用于存储唯一的元素。集合中的元素不能重复&#xff0c;并且没有固定的顺序。在Python 提供了内置的 set 类型来表示集合&#xff0c;所以关键字set就是集合的意思。 你可以使用大括号 {} 或者 …

pytest 生成allure测试报告

allure的安装 github地址 allure资产列表 windows下载.zip&#xff0c;解压并配置环境变量PATH&#xff1b;linux下载tar.gz&#xff0c;解压配置&#xff1b; allure作为pytest插件 # 安装 pip install allure-pytest# 执行单元测试&#xff0c;生成allure测试数据&…

如何在内网中与阿里云服务器进行文件传输?[2024详细版]

随着云计算发展&#xff0c;企业和个人选择将数据存储在云端&#xff0c;以提高数据的安全性和可访问性。阿里云作为国内领先的云服务提供商之一&#xff0c;提供了多种云产品和服务。其中&#xff0c;云服务器ECS&#xff08;Elastic Compute Service&#xff09;因其灵活性和…

网络初识-相关概念

本篇主要介绍关于网络的相关概念~ 相关概念 局域网&#xff1a; 把几个电脑连接到一起&#xff0c;或者几台电脑连接到同一个路由器&#xff0c;就能构成局域网&#xff0c;局域网中的电脑可以相互通信。 广域网&#xff1a; 将多个局域网连接起来&#xff0c;就构成了范围更…

以一种访问权限不允许的方式做了一个访问套接字的尝试

System.Net.Sockets.SocketException: 以一种访问权限不允许的方式做了一个访问套接字的尝试. 近来做的一个net core的网页&#xff0c;突然有这样的一个提示。上网查询之后&#xff0c;有二种可能&#xff0c;1&#xff0c;管理员角色运行VS2022后重新编译一下项目。2&#x…

9.10javaweb项目总结

1.创建吧 这里的话&#xff0c;是用的那个模态背景来写的&#xff0c;就是可以在原有界面上进行创建。目前就只有这些内容&#xff0c;具体一点就是吧吧的分类弄了一下&#xff0c;然后还有待完善&#xff0c;就是贴吧&#xff0c;吧头像的设置&#xff0c;还是有点问题的&…

AutoSar AP平台的SOMEIP文档的理解笔记

1. SOMEIP报文格式 1.1 SOME/IP消息格式&#xff1a;头格式 1.2 SOME/IP头格式&#xff1a;Request ID (Client ID/Session ID) 1.3 SOME/IP头格式&#xff1a;Message Type [8 Bit] and Return Code [8 Bit] 1.4 SOME/IP消息格式的大小端 1.5 SOME/IP消息格式&#xff1a;序列…