预期课程开始时,“ C ++开发人员”准备了有趣的材料的翻译。
Codecademy学习C ++课程的作者Marielle Frank和Sonny Lee最近有机会采访了C ++的创建者BjörnStraustrup博士。在这次采访中,他回答了Stack Overflow上获得最高票数的C ++问题,尽管所有采访都值得一读,但Codecademy慷慨地允许我们分享其中的一部分。如果您想知道关于Stack Overflow的确切答案,那么可以肯定地找到最接近的答案(尽管我们希望有人不同意)。为什么处理排序数组要比处理未排序数组快?
注意:此问题是在所有时间的堆栈溢出中获得最多票数的数字1。
听起来像是采访中的问题。这是真的?你怎么知道的?不进行初步测量就不能回答有关效率的问题,因此知道如何测量它们很重要。因此,我检查了一百万个整数的向量,并得出: 32995
125944
18610
133304
17942
107858
我运行了几次以确保。是的,这种现象是真实的。我的主要代码是:void run(vector<int>& v, const string& label)
{
auto t0 = system_clock::now();
sort(v.begin(), v.end());
auto t1 = system_clock::now();
cout << label
<< duration_cast<microseconds>(t1 — t0).count()
<< " milliseconds\n";
}
void tst()
{
vector<int> v(1'000'000);
iota(v.begin(), v.end(), 0);
run(v, "already sorted ");
std::shuffle(v.begin(), v.end(), std::mt19937{ std::random_device{}() });
run(v, "shuffled ");
}
至少,对于这种编译器,标准库和优化器设置,这种现象是真实的。不同的实现可以给出(和给出)不同的答案。实际上,有人进行了更系统的研究(在Internet上进行快速搜索可以帮助您找到它),并且大多数实现都显示出这种效果。原因之一是分支预测:排序算法中的关键操作是“ if(v [i] <pivot])...”或等效操作。对于排序的序列,此测试始终为true,而对于随机序列,所选分支随机变化。另一个原因是,当向量已经排序时,我们不需要将元素移到正确的位置。这些小细节的影响使我们观察到的系数约为五或六。快速排序(通常是排序)是一项复杂的研究,吸引了一些计算机科学的杰出人士。良好的分类功能是在实施过程中选择良好算法并关注设备性能的结果。如果要编写高效的代码,则需要考虑机器的体系结构。-抱歉。提醒您,Stack Overflow播客又回来了!拜访我们的新任首席执行官并接受采访。什么是C ++中的->运算符?
这是一个古老的技巧问题。在C ++中,没有运算符->。考虑以下:if (p-->m == 0) f(p);
当然,这看起来像是某种运算符->,并带有适当的声明p和m,您甚至可以编译并运行此代码:int p = 2;
int m = 0;
if (p-->m == 0) f(p);
这实际上意味着:查看p--是否大于m(它的方式),然后将结果(真)与0比较。好吧,真!= 0,所以结果为假,并且不调用f()。换一种说法:if ((p--) > m == 0) f(p);
请不要在这些问题上花费太多时间。它们甚至在C ++发明之前就因使初学者困惑而广受欢迎。最佳指南和C ++书籍清单
不幸的是,没有规范的C ++书籍清单。原则上不可能如此。并非每个人都需要相同的信息,也不是每个人都具有相同的经验,并且C ++最佳实践也在不断发展。我在Internet上做了一些研究,发现了一系列惊人的建议。许多已经严重过时了,有些根本不好。寻找没有指导的好书的初学者会很困惑!您确实需要一本书,因为在某些特定主题的博客上很难找到使C ++有效的方法,当然,博客还存在错误,过时和解释不当的问题。通常,他们还专注于高级新主题,而忽略了基本原理。我推荐我的编程:面向刚开始学习编程的人们使用C ++(第二版)的原理和实践,面向已经是程序员并且需要熟悉现代C ++的人们的C ++ Tour(第二版)。具有强大数学背景的人们可以从发现现代C ++开始:面向科学家,工程师和程序员的强化课程 Peter Gottschling。一旦开始真正使用C ++,您将需要一套准则来区分可以做什么和什么是最佳实践。为此,我建议在GitHub上使用C ++核心指南。为了对标准库的各个语言特性和功能有一个很好的简要说明,我建议www.cppreference.com。#4。C ++中的指针变量和引用变量之间有什么区别?
要了解有关链接和指针的更多信息,请查看Learn C ++。两者在内存中均表示为机器地址。不同之处在于它们的用法。要初始化指针,请给它提供对象的地址:int x = 7;
int* p1 = &x;
int* p2 = new int{9};
要读取和写入指针,我们使用解引用运算符(*):*p1 = 9;
int y = *p2;
当我们将一个指针分配给另一个指针时,它们都将指向同一对象:p1 = p2;
*p2 = 99;
int z = *p1;
请注意,指针可以在其生命周期中指向不同的对象。这是链接的主要区别。链接在创建时即被附加到对象上,并且不能转换为另一个链接。对于引用,取消引用是隐式的。使用对象初始化链接,链接将获得其地址。int x = 7;
int& r1 = x;
int& r2 = *new int{9};
new运算符返回一个指针,因此我必须在分配之前取消引用它,使用它来初始化链接。为了通过链接进行读写,我们只需使用链接名称(无需显式解引用):r1 = 9;
int y = r2;
当我们将一个链接分配给另一个链接时,将复制指定的值:r1 = r2;
r1 = 99;
int z = r2;
引用和指针通常都用作函数的参数:void f(int* p)
{
if (p == nullptr) return;
}
void g(int& r)
{
}
int x = 7;
f(&x);
g(x);
可能有一个指针nullptr
,所以我们应该检查它是否指向任何东西。关于链接,您可以最初假定它引用了某些内容。#5。如何遍历字符串?
使用stringstream
,但是如何定义“单词”?想一想:“玛丽有一只小羊羔。” 最后一个词是“羔羊”或“羔羊”。如果没有标点符号,这很容易:vector<string> split(const string& s)
{
stringstream ss(s);
vector<string> words;
for (string w; ss>>w; ) words.push_back(w);
return words;
}
auto words = split("here is a simple example");
for (auto& w : words) cout << w << '\n';
或者简单地:for (auto& w : split("here is a simple example")) cout << w << '\n';
默认情况下,>>运算符将跳过空格。如果我们需要任意的定界符集,情况会更加混乱:template<typename Delim>
string get_word(istream& ss, Delim d)
{
string word;
for (char ch; ss.get(ch); )
if (!d(ch)) {
word.push_back(ch);
break;
}
for (char ch; ss.get(ch); )
if (!d(ch))
word.push_back(ch);
else
break;
return word;
}
d是一个告诉字符是否是定界符的操作,我返回“”(空字符串)以指示没有要返回的单词。vector<string> split(const string& s, const string& delim)
{
stringstream ss(s);
auto del = [&](char ch) { for (auto x : delim) if (x == ch) return true; return false; };
vector<string> words;
for (string w; (w = get_word(ss, del))!= ""; ) words.push_back(w);
return words;
}
auto words = split("Now! Here is something different; or is it? ", "!.,;? ");
for (auto& w : words) cout << w << '\n';
如果您有C ++ 20 Range库,则无需编写类似的内容,但是可以使用split_view。Bjarn Straustrup是纽约摩根士丹利的技术合作伙伴和董事总经理,也是哥伦比亚大学的客座教授。他还是C ++的创建者。
有关C ++ 20的更多信息,请参见:isocpp.org。就这样。在课程中见!