和我一起工作的实习生向我展示了他在计算机科学方面考试的关于字节序问题的考试.有一个问题显示ASCII字符串"My-Pizza",学生必须显示该字符串将如何在小端计算机的内存中表示.当然,这听起来像一个技巧问题,因为ASCII字符串不受端序问题的影响.
但令人震惊的是,实习生声称他的教授坚持认为该字符串将表示为:
P-yM azzi
Run Code Online (Sandbox Code Playgroud)
我知道这不可能是正确的.在任何机器上都不能像ASCII字符串那样表示ASCII字符串.但显然,教授坚持这样做.所以,我写了一个小C程序并告诉实习生把它交给他的教授.
#include <string.h>
#include <stdio.h>
int main()
{
const char* s = "My-Pizza";
size_t length = strlen(s);
for (const char* it = s; it < s + length; ++it) {
printf("%p : %c\n", it, *it);
}
}
Run Code Online (Sandbox Code Playgroud)
这清楚地表明字符串在内存中存储为"My-Pizza".一天后,实习生回到我身边,告诉我教授现在声称C正在自动转换地址,以正确的顺序显示字符串.
我告诉他他的教授很疯狂,这显然是错的.但是为了检查我自己的理智,我决定在stackoverflow上发布这个,所以我可以让其他人确认我在说什么.
所以,我问:谁在这里?
鉴于:
1)C++ 03标准没有以任何方式解决线程的存在
2)C++ 03标准让实现决定是否std::string应该在其拷贝构造函数中使用Copy-on-Write语义
3)Copy-on-Write语义经常导致多线程程序中不可预测的行为
我得出以下看似有争议的结论:
您无法在多线程程序中安全且可移植地使用std :: string
显然,没有STL数据结构是线程安全的.但至少,使用std :: vector,您可以简单地使用互斥锁来保护对向量的访问.使用使用COW的std :: string实现,如果不在供应商实现中深入编辑引用计数语义,您甚至无法可靠地执行此操作.
现实世界的例子:
在我的公司,我们有一个多线程应用程序,经过彻底的单元测试,无数次通过Valgrind.该应用程序运行了几个月,没有任何问题.有一天,我在另一个版本的gcc上重新编译应用程序,突然之间我总是得到随机的段错误.Valgrind现在在std :: string复制构造函数中报告libstdc ++中的无效内存访问.
那么解决方案是什么?好吧,当然,我可以将typedef std::vector<char>作为一个字符串类 - 但实际上,这很糟糕.我还可以等待C++ 0x,我祈祷将要求实现者放弃COW.或者,(颤抖),我可以使用自定义字符串类.我个人总是反对在预先存在的库可以正常运行时实现自己的类的开发人员,但老实说,我需要一个字符串类,我可以肯定它不使用COW语义; 和std :: string根本不保证.
我是不是正确的,std::string根本无法可靠地使用在所有便携式,多线程程序?什么是好的解决方法?
在C++编程领域不时出现的一个常见问题是字节序的编译时确定.通常这是通过几乎不可移植的#ifdef来完成的.但是C++ 11 constexpr关键字和模板专业化是否为我们提供了更好的解决方案?
做以下事情是合法的C++ 11:
constexpr bool little_endian()
{
const static unsigned num = 0xAABBCCDD;
return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD;
}
Run Code Online (Sandbox Code Playgroud)
然后专门为两种endian类型设置模板:
template <bool LittleEndian>
struct Foo
{
// .... specialization for little endian
};
template <>
struct Foo<false>
{
// .... specialization for big endian
};
Run Code Online (Sandbox Code Playgroud)
然后做:
Foo<little_endian()>::do_something();
Run Code Online (Sandbox Code Playgroud) 如何从cout获得无缓冲的输出,以便它立即写入控制台而无需刷新(类似于cerr)?
我认为可以通过rdbuf() - > pubsetbuf完成,但这似乎不起作用.下面的代码片段应该立即输出到控制台,然后等待几秒钟.但相反,它只是等待,只有在程序退出并刷新缓冲区时才输出.
#include <iostream>
int main()
{
std::cout.rdbuf()->pubsetbuf(0, 0);
std::cout << "A";
sleep(5);
}
Run Code Online (Sandbox Code Playgroud) 我最近写了一个函数模板,它引用了一个C数组:
template <class T, size_t N>
void foo(T(&c_array)[N]);
Run Code Online (Sandbox Code Playgroud)
假设T是a char,则C字符串的长度N - 1归因于空终止符.我意识到我应该处理边缘情况N == 0,因为那样的N - 1话std::numeric_limits<std::size_t>::max().
因此,为了避免在有人将零长度数组传递给此函数的罕见情况下可能发生的混乱,我进行了检查N == 0.
然而,令我惊讶的是,似乎零长度数组实际上甚至不是数组类型 - 或者至少,这是GCC似乎相信的.实际上,如果具有指针类型签名的函数可用作候选,则零长度数组甚至不绑定到上述函数签名.
请考虑以下代码:
template <class T, size_t N>
void foo(T(&array)[N])
{
std::cout << "Array" << std::endl;
}
void foo(const void* p)
{
std::cout << "Pointer" << std::endl;
}
int main(int argc, char** argv)
{
char array1[10] = { };
const char* pointer = 0;
char array2[0] = …Run Code Online (Sandbox Code Playgroud) 升级库,以及即将推出的C++ 0x标准,定义了各种类型的特征模板,以区分具有普通构造函数,复制构造函数,赋值或析构函数的对象与不具有普通构造函数,复制构造函数,赋值或析构函数的对象.其中一个最重要的用途是优化某些类型的算法,例如使用memcpy.
但是,我不明白所有各种has_trivial_X模板之间真正的实际区别.C++标准仅定义了我们关注的两大类型:POD和非POD.如果类型具有已定义的构造函数,复制构造函数,赋值运算符或析构函数,则该类型为非POD.换句话说,任何不是内置类型或内置类型的C结构的东西都不是POD.
那么区分例如has_trivial_assign和has_trivial_constructor有什么意义呢?如果一个对象有一个非平凡的赋值运算符或一个非平凡的构造函数,它就不是一个POD.那么在什么情况下知道一个对象有一个简单的赋值运算符,但是一个非平凡的构造函数呢?
换句话说,为什么不定义单个类型特征模板is_pod<T>,并用它完成?
考虑一下这个问题的后续问题.从本质上讲,C++的日期/时间格式化设施似乎无可救药地被打破 - 以至于为了做一些简单的事情,如将日期/时间字符串转换为对象,你真的不得不求助于Boost.Datetime或者旧的C strftime/ strptime设施.
问题是这些解决方案都不能直接使用嵌入特定iostream对象的C++语言环境设置.C工具使用全局C/POSIX语言环境设置,而Boost.Datetime中的I/O工具似乎完全绕过iostream语言环境设置,允许用户直接设置月份,工作日等的名称,而不管语言环境如何.
因此,我想要一些能够尊重特定I/O流上的语言环境设置的东西,这些设置允许我将字符串转换为字符串struct tm.这似乎很容易,但我遇到了各个角落的障碍.起初,我注意到STL的某些实现提供了非标准std::time_get::get函数,所以我决定实现类似的东西.基本上,我只是迭代格式字符串,每当我点击格式标志时,我会使用time_get工具之一(如get_monthname,get_weekday,get_year等)将输入字符串转换为a struct tm.这似乎很容易,除了这些函数中的每一个都需要精确的迭代器范围.您无法转换"Monday,",必须"Monday"完全转换,否则转换失败.因为迭代器必须是istreambuf_iterator,你不能简单地向前扫描,因为每个增量都会改变流缓冲区中的获取位置.所以,基本上你必须首先遍历流,将每个字符复制到另一个 streambuffer,然后当你点击分隔符(如空格或逗号)时,使用带有time_get工具的第二个streambuffer.这就好像C++设计师竭尽全力让它尽可能地烦人.
那么,有更简单的解决方案吗?大多数C++程序员在需要将日期/时间字符串转换为对象时会做什么?我们是否只需要使用C设施,并且失去了在不同的iostream对象上出现的不同语言环境设置所带来的优势?
我们都知道你可以通过在函数内定义局部结构/类来模拟C++ 98中的闭包.但是有一些原因是本地定义的结构不能用于实例化本地范围之外的模板吗?
例如,能够做这样的事情真的很有用:
void work(std::vector<Foo>& foo_array)
{
struct compareFoo
{
bool operator()(const Foo& f1, const Foo& f2) const
{
return f1.bar < f2.bar;
}
};
std::sort(foo_array.begin(), foo_array.end(), compareFoo());
}
Run Code Online (Sandbox Code Playgroud)
如果您知道不需要在代码中的任何其他地方使用compareFoo,那么这将特别有用.但是,唉,这不编译.是否有某些原因导致编译器无法使用本地定义的结构体实例化std :: sort模板函数?
我在一些模板化的代码中遇到了一些烦人的问题 - 最终归结为以下观察:由于某种原因,给定一个STL-ish容器类型T,const typename T::pointer实际上似乎并不是一个常量指针类型,即使T::pointer相当于T::value_type*.
以下示例说明了该问题.假设您有一个模板化函数,它接受一个必须满足STL随机访问容器概念要求的Container.
template <class Container>
void example(Container& c)
{
const typename Container::pointer p1 = &c[0]; // Error if c is const
const typename Container::value_type* p2 = &c[0];
}
Run Code Online (Sandbox Code Playgroud)
然后,如果我们传递这个函数一个const容器......
const std::vector<int> vec(10);
example(vec);
Run Code Online (Sandbox Code Playgroud)
...我们得到一个无效转换const int*到int*.但是为什么const typename Container::pointer和const int*这个例子不一样呢?
请注意,如果我const typename Container::pointer改为简单typename Container::const_pointer编译就好了,但据我所知,const_pointer typedef是一个扩展,(我没有在C++标准容器要求(23.5,表65)中看到它),以及所以我不想用它.
那么如何从容器T中获取通用的,const-correct指针类型呢?(我实在看不出如何做到这一点,而不使用boost :: MPL :: if_与type_traits一起检查,如果容器是不变的......但必须有这样做一个更简洁的方式)
编辑:如果重要,我正在使用gcc 4.3.2来编译它.
为什么基类对象必须是调用派生虚函数的引用?
#include<iostream>
using namespace std;
class A {
public:
virtual void print() { cout << "Hello 1" << endl; }
};
class B : public A {
public:
int x;
void print() { cout << "Hello " << x << endl; }
};
void main(){
B obj1;
A &obj2 = obj1;
A obj3 = obj1; // Why it is different from obj2
obj1.x = 2;
obj1.print();
obj2.print();
obj3.print(); // ?
}
Run Code Online (Sandbox Code Playgroud)