C++面试八股文:什么是左值,什么是右值?

这篇具有很好参考价值的文章主要介绍了C++面试八股文:什么是左值,什么是右值?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

某日二师兄参加XXX科技公司的C++工程师开发岗位第16面:

面试官:什么是左值,什么是右值?

二师兄:简单来说,左值就是可以使用&符号取地址的值,而右值一般不可以使用&符号取地址。

int a = 42;	//a是左值,可以&a
int* p = &a;
int* p = &42;	//42是右值,无法取地址

二师兄:一般左值存在内存中,而右值存在寄存器中。

int a = 42, b = 1024;
decltype(a+b);	//类型为右值,a+b返回的值存在寄存器中
decltype(a+=b);	//类型为左值,a+=b返回的值存储在内存中

二师兄:严格意义上分,右值分为纯右值(pvalue)和将亡值(xvalue)。C++中,除了右值剩余的就是左值。

42;				//纯右值
int a = 1024;
std::move(a);	//将亡值

面试官:C++98/03中已经有了左值,为什么还要增加右值的概念?

二师兄:主要是为了效率。特别是STL中的容器,当需要把容器当作参数传入函数时:

void function(std::vector<int> vi2)
{
    vi2.push_back(6);
    for(auto& i: vi2) { std:: cout < i << " " ;}
    std::cout << std::endl;
}
int main(int argc, char* argv[])
{
    std::vector<int> vi1{1,2,3,4,5};
    function(vi1);
    return 0;
}

二师兄:当我们要把vi1传入函数时,在C++98/03时只能通过拷贝构造函数,把vi1中所有的元素全部拷贝一份给vi2,拷贝完成之后,当function函数返回时,vi2被析构,然后vi1被析构。

二师兄:在C++11及之后,我们可以通过std::move()vi1强制转为右值,此时在初始化vi2时执行的不是拷贝构造而是移动构造:

void function(std::vector<int>&& vi2)
{
    vi2.push_back(6);
    for(auto& i: vi2) { std:: cout < i << " " ;}
    std::cout << std::endl;
}
int main(int argc, char* argv[])
{
    std::vector<int> vi1{1,2,3,4,5};
    function(std::move(vi1));
    return 0;
}

二师兄:这里只进行了一次构造。一次移动(当元素特别多时,移动的成本相对于拷贝基本可以忽略不记),一次析构。效率得到很大的提升。

二师兄:当然,移动过后的变量已经不能再使用(身体被掏空),在std::move(vi1)之后使用vi1是未定义行为。

面试官:好的。那你知道移动构造是如何实现的吗?

二师兄:移动构造是通过移动构造函数实现的,当类有资源需要管理时,拷贝构造会把资源复制一份,而移动构造偷走了原对象的资源。

struct Foo
{
    int* data_;
    
    //copy construct
    Foo(const Foo& oth)
    {
        data_ = new int(*oth.data_);
    }
    //move construct
    Foo(Foo&& oth) noexcept
    {
        data_ = oth.data_;		//steal
        oth.data_ = nullptr;	//set to null
    }
}

面试官:好的。你觉得移动构造函数的noexcept关键字能省略吗?为什么?

二师兄:应该不能吧,具体不清楚。

面试官:那你知道std::move是如何实现的吗?

二师兄:好像是static_cast实现的吧。

面试官:那你知道什么叫万能引用吗?

二师兄:万能引用主要用在模板中,模板参数是T,形参是T&&,此时可以传入任何类型的参数,所以称之为万能引用。

template<typename T>
void function(T&& t) { ...}

面试官:那你知道万能引用是如何实现的吗?

二师兄:不太清楚。。

面试官:完美转发知道吗?

二师兄:std::forward 吗,了解过一些,不太熟悉。

面试官:好的,回去等消息吧。

让我们来回顾以下二师兄今天的表现:

移动构造函数的noexcept关键字能省略吗?为什么?

这里尽量不要省略。如果省略,编译器会推断是否会抛出异常。如果移动构造函数可能会抛出异常,则编译器不会将其标记为noexcept。当编译器不标记为noexcept时,为了保证程序的正确性,编译器可能会采用拷贝构造的方式实现移动构造,从而导致效率降低。

需要注意的是,如果标记了noexcept但在移动时抛出了异常,则程序会调用std::terminate()函数来终止运行。

知道std::move是如何实现的吗?

这里的确是通过static_cast实现的,讲左值强行转换成右值,用来匹配移动语义而非拷贝。

template<typename T>
typename std::remove_reference<T>::type&& move(T&& t) { return static_cast<typename std::remove_reference<T>::type&&>(t);}

万能引用是如何实现的?

万能引用主要使用了引用折叠技术,

template<typename T>
void function(T&& t) { ...}

当T类型为左值时,&& & 被折叠为&, 当T类型为右值时,&& &&被折叠称为&&。以下是折叠规则:

& &    -> &
& &&   -> &
&& &   -> &
&& &&  -> &&

完美转发知道吗?

当我们需要在function中传递t参数时,如何保证它的左值或右值语义呢?这时候完美转发就登场了:

template<typename T>
void function2(T&& t2) {}
template<typename T>
void function(T&& t) 
{
    function2(t);
}

当传入的参数t的类型时右值时,由于引用折叠还是右值,此时的t虽然时一个右值引用,但t本身却是一个左值!这里非常的不好理解。如果我们把t直接传入到function2,那么function2中的t2会被推导成左值,达不到我们的目标。如果在调用function2时传入std::move(t),当t是右值时没有问题,但当t是左值时,把t移动到t2t在外部不在能用。这也不符合我们的预期。此时std::forward闪亮登场!

template<typename T>
void function2(T&& t2) {}
template<typename T>
void function(T&& t) 
{
    function2(std::forward<T&&>(t));
}

std::forward使用了编译时多态(SFINAE)技术,使得当参数t是左值是和右值是匹配不同的实现,完成返回不同类型引用的目的。以下是标准库的实现:

template <typename _Tp>
constexpr _Tp && forward(typename std::remove_reference<_Tp>::type &&__t) noexcept
{
    return static_cast<_Tp &&>(__t);
}

template <typename _Tp>
constexpr typename std::remove_reference<_Tp>::type && move(_Tp &&__t) noexcept
{
    return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
}

好了,今日份面试到这里就结束了。二师兄的表现如何呢?预知后事如何,且听下回分解。

关注我,带你21天“精通”C++!(狗头)文章来源地址https://www.toymoban.com/news/detail-486804.html

到了这里,关于C++面试八股文:什么是左值,什么是右值?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++面试八股文:override和finial关键字有什么作用?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第22面: (二师兄好苦逼,节假日还在面试。。。) 面试官:C++的继承了解吗? 二师兄:(不好意思,你面到我的强项了。。)了解一些。 面试官:什么是虚函数,为什么需要虚函数? 二师兄:虚函数允许在基类中定义一个函

    2024年02月10日
    浏览(31)
  • C++面试八股文:技术勘误

    不知不觉,《C++面试八股文》已经更新30篇了,这是我第一次写技术博客,由于个人能力有限,出现了不少纰漏,在此向各位读者小伙伴们致歉。 为了不误导更多的小伙伴,以后会不定期的出勘误文章,请各位小伙伴留意。 在《C++面试八股文:C++中,设计一个类要注意哪些东

    2024年02月11日
    浏览(35)
  • C++面试八股文:如何避免死锁?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第31面: 面试官:什么是锁?有什么作用? 二师兄:在C++中,锁(Lock)是一种同步工具,用于保护共享资源,防止多个线程同时访问,从而避免数据竞争和不一致。 面试官:有哪些锁? 二师兄:从种类上分,可以分为普通锁、

    2024年02月12日
    浏览(35)
  • C++面试八股文:用过STL吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第21面: 面试官:用过STL吗? 二师兄:(每天都用好吗。。)用过一些。 面试官:你知道STL是什么? 二师兄:STL是指标准模板库( Standard Template Library ),是C++区别于C语言的特征之一。 面试官:那你知道STL的六大部件是什么

    2024年02月09日
    浏览(38)
  • C++面试八股文:聊一聊指针?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第17面: 面试官:聊一聊指针? 二师兄:好的。 面试官:你觉得指针本质上是什么? 二师兄:这要从内存地址开始说起了。如果有一块容量是1G的内存,假设它的地址是从 0x00000000 到 0x3fffffff ,每一个字节都对应一个地址。当

    2024年02月09日
    浏览(29)
  • C++面试八股文:了解位运算吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第12面: 面试官:了解位运算吗? 二师兄:了解一些。(我很熟悉) 面试官:请列举以下有哪些位运算? 二师兄:按位与( )、按位或( | )、按位异或( ^ ),按位取反( ~ )、左移( )和右移( )。 面试官:好的。那你

    2024年02月08日
    浏览(33)
  • java八股文面试[多线程]——什么是守护线程

     知识来源: 【2023年面试】什么是守护线程_哔哩哔哩_bilibili

    2024年02月11日
    浏览(31)
  • C++面试八股文:如何实现一个strncpy函数?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第31面: 面试官: strcpy 函数使用过吧? 二师兄:用过。 面试官:这个函数有什么作用? 二师兄:主要用做字符串复制,将于字符从一个位置复制到另一个位置。 面试官: strncpy 函数也使用过吧,和 strcpy 有何不同? 二师兄:

    2024年02月11日
    浏览(42)
  • C++面试八股文:std::vector了解吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第23面: 面试官: vector 了解吗? 二师兄:嗯,用过。 面试官:那你知道 vector 底层是如何实现的吗? 二师兄: vector 底层使用动态数组来存储元素对象,同时使用 size 和 capacity 记录当前元素的数量和当前动态数组的容量。如果

    2024年02月10日
    浏览(30)
  • C++面试八股文:std::deque用过吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第26面: 面试官: deque 用过吗? 二师兄:说实话,很少用,基本没用过。 面试官:为什么? 二师兄:因为使用它的场景很少,大部分需要性能、且需要自动扩容的时候使用 vector ,需要随机插入和删除的时候可以使用 list 。

    2024年02月11日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包