我知道引用是语法糖,因此代码更容易读写.
但有什么区别?
以下答案和链接摘要:
NULL),而引用总是指对象.&obj + 5).澄清一个误解:
C++标准非常谨慎,以避免规定编译器如何实现引用,但每个C++编译器都将引用实现为指针.也就是说,声明如下:
Run Code Online (Sandbox Code Playgroud)int &ri = i;如果它没有完全优化,则分配与指针相同的存储量,并将地址
i放入该存储中.
因此,指针和引用都使用相同数量的内存.
作为基本规则,
有趣的读物:
当我启用C++ 11时,我在一个小的C++片段中发现了一个有趣的性能回归:
#include <vector>
struct Item
{
int a;
int b;
};
int main()
{
const std::size_t num_items = 10000000;
std::vector<Item> container;
container.reserve(num_items);
for (std::size_t i = 0; i < num_items; ++i) {
container.push_back(Item());
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用g ++(GCC)4.8.2 20131219(预发行版)和C++ 03,我得到:
milian:/tmp$ g++ -O3 main.cpp && perf stat -r 10 ./a.out
Performance counter stats for './a.out' (10 runs):
35.206824 task-clock # 0.988 CPUs utilized ( +- 1.23% )
4 context-switches # 0.116 K/sec ( +- 4.38% )
0 cpu-migrations …Run Code Online (Sandbox Code Playgroud) 我是C++初学者,但不是编程初学者.我正在努力学习C++(c ++ 11),对我来说,最重要的是有点不清楚:传递参数.
我考虑过这些简单的例子:
一个包含其所有成员基本类型的类:
CreditCard(std::string number, int expMonth, int expYear,int pin):number(number), expMonth(expMonth), expYear(expYear), pin(pin)
具有成员基本类型+ 1复杂类型的类:
Account(std::string number, float amount, CreditCard creditCard) : number(number), amount(amount), creditCard(creditCard)
具有成员基本类型的类+具有某种复杂类型的1个集合:
Client(std::string firstName, std::string lastName, std::vector<Account> accounts):firstName(firstName), lastName(lastName), accounts(accounts)
当我创建一个帐户时,我这样做:
CreditCard cc("12345",2,2015,1001);
Account acc("asdasd",345, cc);
Run Code Online (Sandbox Code Playgroud)
显然,在这种情况下,信用卡将被复制两次.如果我重写该构造函数为
Account(std::string number, float amount, CreditCard& creditCard)
: number(number)
, amount(amount)
, creditCard(creditCard)
Run Code Online (Sandbox Code Playgroud)
会有一份副本.如果我把它重写为
Account(std::string number, float amount, CreditCard&& creditCard)
: number(number)
, amount(amount)
, creditCard(std::forward<CreditCard>(creditCard))
Run Code Online (Sandbox Code Playgroud)
将有2个动作,没有副本.
我想有时您可能想要复制一些参数,有时您不希望在创建该对象时进行复制.
我来自C#,用于引用,对我来说有点奇怪,我认为每个参数应该有2个重载,但我知道我错了.
有没有关于如何在C++中发送参数的最佳实践,因为我真的发现它,比方说,并非琐碎.你会如何处理我上面提到的例子?
我正在学习C++,并试着避免养成坏习惯.根据我的理解,clang-tidy包含许多"最佳实践",我尽可能地尽可能地坚持它们(即使我不一定理解为什么它们被认为是好的),但我不确定我是否了解这里推荐的内容.
我在教程中使用了这个类:
class Creature
{
private:
std::string m_name;
public:
Creature(const std::string &name)
: m_name{name}
{
}
};
Run Code Online (Sandbox Code Playgroud)
这导致了一个讽刺的建议,即我应该通过值而不是引用和使用std::move.如果我这样做,我得到的建议做出name的引用(以确保它不会被复制每次),并警告说std::move将不会有任何影响,因为name是const这样,我应该将其删除.
我没有得到警告的唯一方法是const完全删除:
Creature(std::string name)
: m_name{std::move(name)}
{
}
Run Code Online (Sandbox Code Playgroud)
这似乎是合乎逻辑的,因为唯一的好处const是防止弄乱原始字符串(这不会因为我通过值传递而发生).但我在CPlusPlus.com上看到了:
虽然注意到-in标准库移动意味着移动的对象保持有效但未指定的状态.这意味着,在这样的操作之后,移动对象的值应该只被销毁或分配一个新值; 访问它否则会产生一个未指定的值.
现在想象一下这段代码
std::string nameString("Alex");
Creature c(nameString);
Run Code Online (Sandbox Code Playgroud)
因为nameString通过值传递,std::move只会name在构造函数内部无效而不会触及原始字符串.但这有什么好处?看起来无论如何内容只被复制一次 - 如果我在调用时通过引用传递m_name{name},如果我在传递它时传递值(然后它被移动).我知道这比通过值而不是使用std::move(因为它被复制两次)更好.
所以有两个问题:
std::move过度引用和调用是否有任何好处m_name{name}?在Herb Sutter的CppCon 2014谈话回归基础:现代C++风格他在幻灯片28(幻灯片的网络副本在这里)上引用了这种模式:
class employee {
std::string name_;
public:
void set_name(std::string name) noexcept { name_ = std::move(name); }
};
Run Code Online (Sandbox Code Playgroud)
他说这是有问题的,因为当用临时调用set_name()时,noexcept-ness不强(他使用短语"noexcept-ish").
现在,我在我最近的C++代码中使用了上述模式,主要是因为它节省了我每次都输入两个set_name()的副本 - 是的,我知道每次强制复制构造都会有点效率低下,但是,嘿,我是一个懒惰的人.然而Herb的短语"这个noexcept是有问题的 "让我担心,因为我没有在这里得到问题:std :: string的移动赋值运算符是noexcept,因为它的析构函数,所以上面的set_name()似乎保证noexcept.我确实看到编译器在 set_name()之前抛出了一个潜在的异常,因为它准备了参数,但我很难看到它有问题.
后来在幻灯片32上Herb明确指出上面是一个反模式.有人可以向我解释为什么我一直懒惰地编写糟糕的代码?
如我错了请纠正我.说我有:
struct X
{
std::string mem_name;
X(std::string name)
: mem_name(std::move(name))
{}
...
};
struct Y
{
std::string mem_name;
Y(const std::string &name)
: mem_name(name)
{}
...
};
Run Code Online (Sandbox Code Playgroud)
在Xctor中,name显然是传递给任何参数的副本X,X调用std::string初始化的移动mem_name,对吧?
让我们称之为X*的复制然后移动 ; 两个操作:COPY,MOVE.
在Y's ctor中,name是一个const ref,这意味着没有元素的实际副本,因为我们直接处理从Y需要创建对象的地方传递的参数.但是,我们复制name到初始化mem_name的Y; 一个操作:COPY.因此它应该更快(对我来说更好)?
在Scott Meyer的GN13演讲中(围绕时间框架8:10和8:56),他谈到了"想要速度?通过价值传递",我想知道在传递参数时是否有任何性能差异或损失(或者字符串是准确的)通过引用和传递值"以获得速度?"
我知道按值传递参数可能很昂贵,特别是在处理大数据时.
也许(显然?)我的谈话中缺少一些东西?
我已经看到它说一个operator=带有相同类型by-value的参数的文件在C++ 11中既作为复制赋值运算符又作为移动赋值运算符:
Foo& operator=(Foo f)
{
swap(f);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
替代方案的重复次数将超过两倍,并且可能出现错误:
Foo& operator=(const Foo& f)
{
Foo f2(f);
swap(f2);
return *this;
}
Foo& operator=(Foo&& f)
{
Foo f2(std::move(f));
swap(f2);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
在什么情况下,ref-to-const和r-value重载优先通过值,或何时需要?我正在考虑std::vector::push_back,例如,它被定义为两个重载:
void push_back (const value_type& val);
void push_back (value_type&& val);
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,pass by value 用作复制赋值运算符和移动赋值运算符,无法push_back在Standard中定义为单个函数?
void push_back (value_type val);
Run Code Online (Sandbox Code Playgroud) MSDN文章如何:编写移动构造函数具有以下建议.
如果为类提供移动构造函数和移动赋值运算符,则可以通过编写移动构造函数来调用移动赋值运算符来消除冗余代码.以下示例显示了调用移动赋值运算符的移动构造函数的修订版本:
// Move constructor.
MemoryBlock(MemoryBlock&& other)
: _data(NULL)
, _length(0)
{
*this = std::move(other);
}
Run Code Online (Sandbox Code Playgroud)
这个代码是通过双重初始化MemoryBlock的值来实现低效的,还是编译器能够优化掉额外的初始化?我是否应该通过调用移动赋值运算符来编写移动构造函数?
Constantness
class MyClass {
// ...
private:
std::string m_parameter;
// ...
}
Run Code Online (Sandbox Code Playgroud)
通过按值:
void MyClass::SetParameter(std::string parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
传址参考:
void MyClass::SetParameter(std::string& parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
传址常量-REF:
void MyClass::SetParameter(const std::string& parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
传址常量值:
void MyClass::SetParameter(const std::string parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
传址通用-REF:
void MyClass::SetParameter(std::string&& parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
传址常量万能-REF:
void MyClass::SetParameter(const std::string&& parameter)
{
m_parameter = parameter;
}
Run Code Online (Sandbox Code Playgroud)
哪种变体最好(可能就C++ 11及其移动语义而言)?
PS.可能是函数体在某些情况下是不正确的.
我认为具有通用引用参数的构造函数比没有引用的构造函数具有更好的性能。
From cppreference (https://en.cppreference.com/w/cpp/utility/functional/function/function), we can see that the template constructor for std::function is not using reference.
template< class F > function( F f );
Run Code Online (Sandbox Code Playgroud)
Is it a mistake? If not, why the standard doesn't requires the constructor use universal reference?
EDIT:
Let's think about two cases.
Using universal reference:
lvalue case: 4 bytes are copied.
rvalue case: 4 bytes are copied. (Will you …
我正在尝试更改我的代码以使用 std::move 按值获取向量,而不是按引用传递它,因为我已经收集到这样会更有效。不过,我已经看到了不同的方法,一种是让构造函数按值传递并在构造函数中使用 std::move,另一种方法是用 std::move 初始化类并让构造函数采用右值(我做对了吗?)。下面的一些例子:
方法一:
构造函数:
StatisticsCompiler::StatisticsCompiler(std::vector<Wrapper<StatisticsMC>> Inner_) :Inner(std::move(Inner_))
{
}
Run Code Online (Sandbox Code Playgroud)
在主要:
vector<Wrapper<StatisticsMC>> gathererArray{ meanGatherer, absQuantileGatherer, relVaRGatherer, relESGatherer };
StatisticsCompiler gathererCombiner(gathererArray);
Run Code Online (Sandbox Code Playgroud)
方法二。
构造函数:
StatisticsCompiler::StatisticsCompiler(std::vector<Wrapper<StatisticsMC>>&& Inner_) :Inner(Inner_)
{
}
Run Code Online (Sandbox Code Playgroud)
主要的:
vector<Wrapper<StatisticsMC>> gathererArray{ meanGatherer, absQuantileGatherer, relVaRGatherer, relESGatherer };
StatisticsCompiler gathererCombiner(std::move(gathererArray));
Run Code Online (Sandbox Code Playgroud)
这里发生的事情之间是否有区别,或者是同一件事,第一种方法在 main 中“看起来”更好,但第二种方法是我直观地理解它以学习右值的方式。如果在性能方面它们完全相同,那么标准做法是什么?