当前位置: 首页 > news >正文

做网站买主机还是服务器简述网络营销的含义

做网站买主机还是服务器,简述网络营销的含义,导航网址网站怎么做,怎么把网站做10万ip这里写目录标题 左值右值左值引用和右值引用右值引用和移动构造函数std::move 移动语义返回值优化移动操作要保证安全 万能引用std::forward 完美转发传入左值传入右值 左值 左值是指可以使用 & 符号获取到内存地址的表达式,一般出现在赋值语句的左边&#xff…

这里写目录标题

      • 左值
      • 右值
      • 左值引用和右值引用
      • 右值引用和移动构造函数
        • std::move 移动语义
        • 返回值优化
        • 移动操作要保证安全
      • 万能引用
      • std::forward 完美转发
          • 传入左值
          • 传入右值

左值

左值是指可以使用 & 符号获取到内存地址的表达式,一般出现在赋值语句的左边,比如变量、数组元素和指针等。

下面是左值的举例:

int i = 42; // i 是一个 left value
int *p = &i // i 是一个左值,可以通过 & 获取内存地址
int& ldemoFoo() {return i;
}ldemoFoo() = 42;//  这里使用函数返回引用的形式, ldemoFoo 是一个左值
int *p1 = &ldemoFoo() 

左值(lvalue)代表的是对象的身份和它在内存中的位置,它一般出现在赋值语句的左侧,左值通常是可修改的(可以修改的左值)。
Notice : 左值代表一个具体的对象位置,在内存中有明确位置,通常是可以修改的

左值的特点包括:

  • 可寻址性:左值可以取得地址,即你可以使用 & 运算符来取得一个左值的地址。
  • 可赋值性:左值可以出现在赋值语句的左侧,你可以将一个值赋给它。

下面类型的值都是左值:

  • 变量名:如 int x;,x是一个左值。
  • 数组元素:如 arr[0],arr[0] 是一个数组的左值元素。
  • 结构体或类的成员:如 obj.member ,obj.member 是一个对象的左值成员。
  • 解引用的指针:如 *ptr,*ptr 是通过指针访问的对象的左值。

并非所有的左值都是可修改的。例如,const 限定的左值就不应该被修改。

右值

在C++中,右值(rvalue)是指哪些不代表内存的中具体位置, 不能被赋值和取地址的值 。
一般出现在赋值操作符的右边,表达式结束就不存在的临时变量。

右值的典型例子包括 字面量、临时对象以及某些表达式的结果。
右值主要用来表示数据值本身,而不是数据所占据的内存位置。

右值的关键特性包括:

  • 不可寻址性:右值不能取得地址,尝试对右值使用 & 运算符会导致编译错误。
  • 可移动性: 由于右值不代表持久的内存位置,因此可以安全地 “移动” 它们的资源到另一个对象,而无需进行复制。这就是为什么右值经常与移动语义一起使用。
  • 临时性:许多右值是临时对象,它们在表达式结束后就会被销毁。

Notice:右值必须要有一个包含内存地址变量去接收这个指,否则就会丢弃

C++ 中右值的例子有:

  • 字面量:比如整数10、字符’A’、浮点数3.14。
  • 函数返回的临时值:如 getRandomNumber() 返回的随机数,注意函数也有可能返回左值。
  • 由运算符产生的值:比如表达式 a + b 的结果,假设 a 和 b 是数值类型的变量。
  • 空指针常量:nullptr。
  • 字符串字面量:比如"hello world"。
  • 类的右值构造函数或移动构造函数生成的临时对象:如 MyClass() 创建的临时对象。
  • 通过std::move()转换得到的右值:std::move(myObject),其中 myObject 是一个左值。
  • 数组下标的表达式:如果数组是右值,那么数组下标也是右值,例如 arr[0],其中arr是一个临时数组。
  • 类成员的右值访问:如果类有一个返回右值的成员函数,那么该函数返回的结果是右值。
// 10 'A' 都是右值字面量
int a = 10;
char b = 'A';//  generateResult 返回值是一个临时对象,也就是右值
int a = generateResult(20, 10);
// a + b 产生的结果也是右值
int m = a + b;
// "hello world "是右值
const char *pName = "hello world";
// nullptr 是右值
int32_t *p = nullptr;DemoClass p = DemoClass();

注意函数返回值不一定只能是右值,也有可能是左值,比如返回引用的情形

int& testlvaluefuncyion() {int i;return i;
}int testrvaluefuncyion() {int i = 5;return i;
}{testlvaluefuncyion();// 正确,函数返回值可作为左值testlvaluefuncyion() = 10;int *p1 = &testlvaluefuncyion();std::cout << "function return value as leftvalue" << std::endl;
}// 函数返回值是int类型,此时只能作为右值
{testrvaluefuncyion();//testrvaluefuncyion() = 10;std::cout << "function return value as rightvalue" << std::endl;
}

左值引用和右值引用

C++中的引用是一种别名,代表的就是变量的地址本身,可以通过一个变量别名访问一个变量的值。
int &a = b 表示可以通过引用 a 访问变量 b , 注意引用实际就是指向变量 b,等于是变量 b 的别名
左值引用是指对左值进行引用的引用类型,通常使用 & 符号定义
右值引用是指对右值进行引用的引用类型,通常使用 && 符号定义

C++11引入了右值引用,允许我们将右值绑定到引用上。这在 移动语义完美转发 等高级功能中非常有用。

class DemoClass {...};
// 接收一个左值引用
void foo(X& x);
// 接收一个右值引用
void foo(X&& x);X x;
foo(x); // 传入参数为左值,调用foo(X&);X bar();
foo(bar()); // 传入参数为右值,调用foo(X&&);

通过重载左值引用和右值引用两种函数版本,满足在传入左值和右值时触发不同的函数分支。 注意 void foo(const X& x); 同时接受左值和右值传参。

void foo(const X& x);
X x;
foo(x); // ok, foo(const X& x)能够接收左值传参X bar();
foo(bar()); // ok, foo(const X& x)能够接收右值传参// 新增右值引用版本
void foo(X&& x);
foo(bar()); // ok, 精准匹配调用foo(X&& x)

定义右值引用的方法:

int a = 10;
// 定义左值引用
int &lvalue_ref = a; // 定义右值引用
int &&rvalue_ref = 10 + 20; 

右值引用和移动构造函数

假设定义一个类 DemoContainerClass,包含一个指针成员变量 p,该指针指向了另一个成员变量 DemoBasicClass,假设 DemoBasicClass 占用了很大的内存,创建和复制 DemoBasicClass 都需要很大的开销。

class DemoBasicClass {
public:DemoBasicClass() {std::cout << __FUNCTION__ "construct call" << std::endl;}~DemoBasicClass() = default;DemoBasicClass(const DemoBasicClass& ref) {std::cout << __FUNCTION__ "copy construct call" << std::endl;}
};class DemoContainerClass{
private:DemoBasicClass *p = nullptr;
public:DemoContainerClass() {p = new DemoBasicClass();}
~DemoContainerClass() {if( p != nullptr) {delete p;}
}
DemoContainerClass(const DemoContainerClass& ref) {std::cout << __FUNCTION__ "copy construct call" << std::endl;p = ref.p;
}DemoContainerClass& operator=(const DemoContainerClass& ref) {std::cout << __FUNCTION__ "operator construct call" << std::endl;DemoBasicClass* tmp = new DemoBasicClass(*ref.p);delete this->p;this->p = tmp;return *this;
}

上面定义了 DemoContianerClass 的赋值构造函数 ,现在假设有下面的场景

{DemoContainerClass p;DemoContainerClass q;p = q;
}

输出如下:

rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =operator construct call
rValurRefDemo::DemoBasicClass::DemoBasicClasscopy construct call

DemoContainerClass pDemoContainerClass q 初始化时,都会执行 new DemoBasicClass,所以会调用两次 DemoBasicClassconstruct ,执行 p = q 时,会调用一次 DemoBasicClass 的拷贝构造函数,根据 ref 复制出一个新结果。
由于 q 在后面的场景还是可能使用的,为了避免影响 q,在赋值的时候调用DemoBasicClass 的构造函数复制出一个新的 DemobasicClass 给 p 是没有问题的。

但在下面的场景下,这样是没有必要的

static rValurRefDemo::DemoContainerClass demofunc() {return rValurRefDemo::DemoContainerClass();
}{DemoContainerClass p;p = demofunc();
}

这种场景下,demofunc 创建的那个临时对象在后续的代码中是不会用到的,所以我们不需要担心赋值函数中会不会影响到那个 DemobasicClass 临时对象,也就没有必要创建一个新的 DemoBasicClass 类型给 p,
更高效的做法是,直接使用 swap 交换对象的 p 指针,这样做有两个好处:

  1. 不需要调用 DemobasiClass 的构造函数,提高效率
  2. 交换之后,demofunc 返回的临时对象拥有 p 对象的 p 指针,在析构时可以自动回收,避免内存泄漏

这种避免高昂的复制成本,从而直接将资源从一个对象移动到另一个对象的行为,就是C++的 移动语义
哪些场景适合移动操作呢?无法获取内存地址的右值就很合适,我们不需要担心后续的代码会用到这个值。
添加移动赋值构造函数如下:

DemoContainerClass& operator=(DemoContainerClass&& rhs) noexcept {std::cout << __FUNCTION__ "move construct call" << std::endl;std::swap(this->p, rhs.p);return *this;
};

输出结果如下:

###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call
std::move 移动语义

C++提供了std::move函数,这个函数做的工作很简单:通过隐藏掉入参的名字,返回对应的右值。

std::cout << "#############################################" << std::endl;
{DemoContainerClass p;DemoContainerClass q;// OK 返回右值,调用移动赋值构造函数 q,但是 q 以后都不能正确使用了p = std::move(q);}std::cout << "#######################################" << std::endl;
{DemoContainerClass p;// OK 返回右值,调用移动赋值构造函数 效果和 demofunc 一样p = std::move(demofunc());
}

输出结果如下:

###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call
###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call

一个容易犯错的例子:

class Base {
public:// 拷贝构造函数Base(const Base& rhs);// 移动构造函数Base(Base&& rhs) noexcept;
};class Derived : Base {
public:Derived(Derived&& rhs)// wrong. rhs是左值,会调用到 Base(const Base& rhs).// 需要修改为Base(std::move(rhs)): Base(rhs) noexcept {...}
}
返回值优化

考虑下面的情形:

DemobasicClass foo() {DemobasicClass  x;return x;
};DemobasicClass  bar() {DemobasicClass  x;return std::move(x);
}

大家可能会觉得 foo 需要一次复制行为:从 x 复制到返回值;bar 由于使用了 std::move,满足移动条件,所以触发的是移动构造函数:从x移动到返回值。复制成本大于移动成本,所以 bar 性能更好。

实际效果与上面的推论相反,bar中使用std::move反倒多余了。现代C++编译器会有返回值优化。换句话说,编译器将直接在foo返回值的位置构造x对象,而不是在本地构造x然后将其复制出去。很明显,这比在本地构造后移动效率更快。

移动操作要保证安全

比较经典的场景是std::vector 扩缩容。当vector由于push_back、insert、reserve、resize 等函数导致内存重分配时,如果元素提供了一个 noexcept 的移动构造函数,vector 会调用该移动构造函数将元素移动到新的内存区域;否则 则会调用拷贝构造函数,将元素复制过去。

万能引用

完美转发是C++11引入的另一个与右值引用相关的高级功能。它允许我们在函数模板中将参数按照原始类型(左值或右值)传递给另一个函数,从而避免不必要的拷贝和临时对象的创建。

为了实现完美转发,我们需要使用 std::forward 函数和通用引用(也称为转发引用)。通用引用是一种特殊的引用类型,它可以同时绑定到左值和右值。通用引用的语法如下:

通用引用的形式如下:

template<typename T>
void foo(T&& param);

万能引用的ParamType是T&&,既不能是const T&&,也不能是std::vector&&

通用引用的规则有下面几条:

  1. 如果 expr 是左值, T 和 param 都会被推导为左值引用
  2. 如果 expr 是右值, T会被推导成对应的原始类型, param会被推导成右值引用(注意,虽然被推导成右值引用,但由于param有名字,所以本身还是个左值)。
  3. 在推导过程中,expr的const属性会被保留下来。

参考下面示例:

template<typename T>
void foo(T&& param);// x是一个左值
int x =2 7;
// cx 是带有const的左值
const int cx = x;
// rx 是一个左值引用
const int& rx = cx;// x是左值,所以T是int&,param类型也是int&
foo(x);// cx是左值,所以T是const int&,param类型也是const int&
foo(cx);// rx是左值,所以T是const int&,param类型也是const int&
foo(rx);// 27是右值,所以 T 是int,param类型就是 int&&
foo(27);

std::forward 完美转发

template<typename T, typename Arg> 
std::shared_ptr<T> factory_v4(Arg&& arg)
{ return std::shared_ptr<T>(new T(std::forward<Arg>(arg)));
}//  std::forward的定义如下
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{return static_cast<S&&>(a);
}
传入左值

int p;
auto a = factory_v4§;

根据万能引用的推导规则,factory_v4中的 Arg 会被推导成 int&。这个时候factory_v4 和 std::forwrd等价于:

shared_ptr<A> factory_v4(int& arg)
{ return shared_ptr<A>(new A(std::forward<int&>(arg)));
}int& std::forward(int& a) 
{return static_cast<int&>(a);
}

这时传递给 A 的参数是 int&, 调用的是拷贝构造函数 A(int& ref), 符合预期

传入右值

auto a = factory_v4(3);

shared_ptr<A> factory_v4(int&& arg)
{ return shared_ptr<A>(new A(std::forward<int&&>(arg)));
}int&& std::forward(int&& a) 
{return static_cast<int&&>(a);
}

此时,std::forward作用与std::move一样,隐藏掉了arg的名字,返回对应的右值引用。
这个时候传给A的参数类型是X&&,即调用的是移动构造函数A(X&&),符合预期。
右值引用,移动构造
####重要参考
深浅拷贝和临时对象

http://www.zhongyajixie.com/news/56548.html

相关文章:

  • 西安网站建设技术外包软件编程培训学校排名
  • 做网站需要人员宁波优化seo软件公司
  • 商业网站建设设计排名查询系统
  • 帮我们公司做网站app推广渠道商
  • android聊天功能开发网站优化的意义
  • 那个比特币网站可以做杠杆软件开发公司有哪些
  • 做相册的网站dw军事新闻
  • 哈尔滨网站制作前景中囯军事网
  • 中国旅游电子商务网站建设情况策划方案模板
  • 起飞页做网站步骤logo网站设计
  • 同ip多域名做同行业网站企业seo关键词优化
  • 做的网站空白了深圳网络营销
  • 长沙微信营销公司江苏搜索引擎优化
  • php网站源码架构seo是什么意思 为什么要做seo
  • 上海网站建设找缘魁免费信息发布平台网站
  • 如何做网站主页seo服务 文库
  • 企业建站一条龙官网seo哪家公司好
  • 佛山小企业网站建设2021年网络营销考试题及答案
  • 榆林市 网站建设买淘宝店铺多少钱一个
  • diy网站源码百度推广的五大优势
  • 进入网站后台管理系统360竞价推广登录入口
  • wordpress支持的语言包搜索引擎关键词优化
  • 对网站建设的认识百度网盘网页版登录入口官网
  • 特产网站源码深圳网络推广公司
  • 电商模板下载的网站chrome网页版入口
  • 盐步网站制作网上推广
  • 日本做仿牌网站黄页网站推广公司
  • 河北邢台福州seo顾问
  • 360网站seo手机优化软件黄页网推广服务
  • 政府网站集约化建设的目的济南疫情最新消息