C++;结构替换C结构 与我团队中的新开发人员讨论后,我意识到C++中仍然存在使用C结构的习惯,因为它们应该更好(即更快、更精简、更美观、更适合您的理由)。 哪些值得分享的例子,显示了C构造,与类似C++构造相比?>/P> 对于每个例子,我需要阅读C++构造与原C构造一样好或更好的原因。目的是提供一些C++构造的替代方案,这些C++构造在C++代码中被认为有危险性/不安全性(C++ + 0x有效的,只有被明确地标记为c++0x的唯一答案才被接受)。
我将在下面发布一个答案(结构内联初始化)作为示例 注1:请每个案例回答一个问题。如果您有多个案例,请发布多个答案 注2:这不是一个C问题。不要将“C”标记添加到此问题。这并不是C++与C之间的冲突,而是对C++ C++子集的一些构造的研究,以及它们在其他C++“工具包”中的替代。 注3:这不是一个C-bashing问题。我想要理由。吹嘘、抨击和未经证实的比较将被淡化。提到C++特性没有C等价物可以被考虑为主题:我希望并排放置C特性来对抗C++特性。 有时候,我们需要C++中简单的数据聚合。由于数据具有一定的独立性,因此通过封装来保护数据是不值得的C++;结构替换C结构 与我团队中的新开发人员讨论后,我意识到C++中仍然存在使用C结构的习惯,因为它们应该更好(即更快、更精简、更美观、更适合您的理由)。 哪些值得分享的例子,显示了C构造,与类似C++构造相比?>/P> 对于每个例子,我需要阅读C++构造与原C构造一样好或更好的原因。目的是提供一些C++构造的替代方案,这些C++构造在C++代码中被认为有危险性/不安全性(C++ + 0x有效的,只有被明确地标记为c++0x的唯一答案才被接受)。,c++,c,language-construct,C++,C,Language Construct,我将在下面发布一个答案(结构内联初始化)作为示例 注1:请每个案例回答一个问题。如果您有多个案例,请发布多个答案 注2:这不是一个C问题。不要将“C”标记添加到此问题。这并不是C++与C之间的冲突,而是对C++ C++子集的一些构造的研究,以及它们在其他C++“工具包”中的替代。 注3:这不是一个C-bashing问题。我想要理由。吹嘘、抨击和未经证实的比较将被淡化。提到C++特性没有C等价物可以被考虑为主题:我希望并排放置C特性来对抗C++特性。 有时候,我们需要C++中简单的数据聚合。由于数
// C-like code in C++
struct CRect
{
int x ;
int y ;
} ;
void doSomething()
{
CRect r0 ; // uninitialized
CRect r1 = { 25, 40 } ; // vulnerable to some silent struct reordering,
// or adding a parameter
}
);
我发现上述代码存在三个问题:
// C++
struct CRect
{
CRect() : x(0), y(0) {} ;
CRect(int X, int Y) : x(X), y(Y) {} ;
int x ;
int y ;
} ;
void doSomething()
{
CRect r0 ;
CRect r1(25, 40) ;
}
- 如果对象没有被专门初始化,它将根本不会被初始化
- 如果我们更改x或y(无论出于何种原因),doSomething()中默认的C初始化现在将是错误的
- 如果我们添加一个z成员,并希望它在默认情况下为“零”,那么我们仍然需要更改每个内联成员
C99允许命名初始值设定项: 正确的r={.x=25,.y=40}
这不会在C++中编译。我猜这应该添加到C++,如果只是C兼容的话。无论如何,在C语言中,它缓解了这个答案中提到的问题。
几乎所有使用void*
宏与内联模板的情况
C风格:
#define max(x,y) (x) > (y) ? (x) : (y)
C++风格
inline template<typename T>
const T& max(const T& x, const T& y)
{
return x > y ? x : y;
}
内联模板
常数T和最大值(常数T和x、常数T和y)
{
返回x>y?x:y;
}
<>理由:喜欢C++方法:
- 类型安全性——强制参数必须是相同的类型
- max定义中的语法错误将指向正确的位置,而不是调用宏的位置
- 可以调试成函数
Resource
可以是指向内存的指针,Acquire/Release将分配/释放该内存,也可以是打开的文件描述符,Acquire/Release将在其中打开/关闭该文件
这带来了一些问题:
Release
r
的数据流的信息。如果r
是在同一范围内获取和发布的,则代码不会自行记录此信息资源r
和r.Acquire(…)
之间的时间内,r
实际上是可访问的,尽管未初始化。这是bug的来源应用RAII(资源获取初始化)方法,在C++中获得
class ResourceRAII
{
Resource rawResource;
public:
ResourceRAII(...) {rawResource = Acquire(...);}
~ResourceRAII() {Release(rawResource);}
// Functions for manipulating the resource
};
...
{
ResourceRAII r(...);
... Code that uses r ...
}
C++版本将确保您不会忘记释放资源(如果您确实存在,则内存泄漏,这更容易被调试工具检测)。它迫使程序员明确说明资源的数据流是如何流动的(即:如果它只存在于函数的作用域中,则可以通过在堆栈上构造ResourceRAII来明确这一点)。在创建资源对象和销毁资源对象之间没有资源无效的时间点
它也很安全 iostream vs stdio.h 在C中:#包括
int main()
{
int num=42;
printf(“%s%d%c”,“Hello World\n”,num,'\n');
返回0;
}
格式字符串在运行时被解析,这意味着它不是类型安全的
在C++中:
#include <iostream>
int main()
{
int num = 42;
std::cout << "Hello World\n" << num << '\n';
}
#包括
int main()
{
int num=42;
std::cout动态数组与STL容器
C型:
int **foo = new int*[n];
for (int x = 0; x < n; ++x) foo[x] = new int[m];
// (...)
for (int x = 0; x < n; ++x) delete[] foo[x];
delete[] foo;
int**foo=newint*[n];
对于(int x=0;x
C++风格:
std::vector< std::vector<int> > foo(n, std::vector<int>(m));
// (...)
std::vectorfoo(n,std::vector(m));
// (...)
为什么STL容器更好:
- 它们可以调整大小,阵列大小固定
- 它们是异常安全的—若未处理的异常发生在(…)部分,那个么阵列内存可能会泄漏—容器是在堆栈上创建的,所以它将在展开期间被正确销毁
- 它们实现绑定检查,例如(在数组上越界很可能会产生访问冲突并终止程序)
- 它们更易于使用,例如与手动清除阵列相比
- 他们藏起来了
#include <stdio.h>
int main()
{
int num = 42;
printf("%s%d%c", "Hello World\n", num, '\n');
return 0;
}
#include <iostream>
int main()
{
int num = 42;
std::cout << "Hello World\n" << num << '\n';
}
int **foo = new int*[n];
for (int x = 0; x < n; ++x) foo[x] = new int[m];
// (...)
for (int x = 0; x < n; ++x) delete[] foo[x];
delete[] foo;
std::vector< std::vector<int> > foo(n, std::vector<int>(m));
// (...)
#define MYBUFSIZE 256
. . .
char somestring[MYBUFSIZE];
const int MYBUFSIZE = 256;
char somestring[MYBUFSIZE];
int pint_less_than(void const* pa, void const* pb) {
return *static_cast<int const*>(pa) - *static_cast<int const*>(pb);
}
struct greater_than {
bool operator ()(int a, int b) {
return a > b;
}
};
template <std::size_t Size>
void print(int (&arr)[Size]) {
std::copy(arr, arr + Size, std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
int main() {
std::size_t const size = 5;
int values[] = { 4, 3, 6, 8, 2 };
{ // qsort
int arr[size];
std::copy(values, values + size, arr);
std::qsort(arr, size, sizeof(int), &pint_less_than);
print(arr);
}
{ // sort
int arr[size];
std::copy(values, values + size, arr);
std::sort(arr, arr + size);
print(arr);
}
{ // sort with custom comparer
int arr[size];
std::copy(values, values + size, arr);
std::sort(arr, arr + size, greater_than());
print(arr);
}
}
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])
char src[23];
int dest[ARRAY_SIZE(src)];
template <typename T, size_t size>
inline size_t array_size(T (&p)[size])
{
// return sizeof(p)/sizeof(p[0]) ;
return size ; // corrected after Konrad Rudolph's comment.
}
#include <iostream>
// C-like macro
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])
// C++ replacement
template <typename T, size_t size>
inline size_t array_size(T (&p)[size])
{
// return sizeof(p)/sizeof(p[0]) ;
return size ; // corrected after Konrad Rudolph's comment.
}
int main(int argc, char **argv)
{
char src[23];
char * src2 = new char[23] ;
int dest[ARRAY_SIZE(src)];
int dest2[array_size(src)];
std::cout << "ARRAY_SIZE(src) : " << ARRAY_SIZE(src) << std::endl ;
std::cout << "array_size(src) : " << array_size(src) << std::endl ;
std::cout << "ARRAY_SIZE(src2) : " << ARRAY_SIZE(src2) << std::endl ;
// The next line won't compile
//std::cout << "array_size(src2) : " << array_size(src2) << std::endl ;
return 0;
}
ARRAY_SIZE(src) : 23
array_size(src) : 23
ARRAY_SIZE(src2) : 4
/main.cpp|539|error: no matching function for call to ‘array_size(char*&)’|
inline template <typename T, size_t size>
constexpr size_t array_size(T (&p)[size])
{
//return sizeof(p)/sizeof(p[0]) ;
return size ; // corrected after Konrad Rudolph's comment.
}
template<typename T, size_t N> char (& array_size(T(&)[N]) )[N];
int p[] = { 1, 2, 3, 4, 5, 6 };
int u[sizeof array_size(p)]; // we get the size (6) at compile time.
AddUserName(int userid, NameInfo nameinfo);
AddUserAge(int userid, int iAge);
AddUserAddress(int userid, AddressInfo addressinfo);
User::AddInfo(NameInfo nameinfo);
User::AddInfo(int iAge);
User::AddInfo(AddressInfo addressInfo);
int main(void)
{
for(int i = 0; i < 10; i++)
...
int r = 0;
return r;
}
void PrintToScreen(const char *pBuffer);
void PrintToFile(const char *pBuffer);
void PrintToSocket(const char *pBuffer);
void PrintPrettyToScreen(const char *pBuffer);
namespace Screen
{
void Print(const char *pBuffer);
}
namespace File
{
void Print(const char *pBuffer);
}
namespace Socket
{
void Print(const char *pBuffer);
}
namespace PrettyScreen
{
void Print(const char *pBuffer);
}
int fibonacci() {
static int a0 = 0, a1 =1; // problematic....
int temp = a0;
a0 = a1;
a1 = temp + a0;
return temp;
}
void Graph( (int)(*func)(void) );
void Graph2( (int)(*func1)(void), (int)(*func2)(void) );
Graph(fibonacci);
Graph2(fibonacci,fibonacci);
class Fib {
public:
Fib() : a0_(1), a1_(1) {}
int operator();
private:
int a0_, a1_;
};
int Fib::operator() {
int temp = a0_;
a0_ = a1_;
a1_ = temp + a0_;
return temp;
}
template <class FuncT>
void Graph( FuncT &func );
template <class FuncT>
void Graph2( FuncT &func1, FuncT &func2);
Fib a,b,c;
Graph(a);
Graph2(b,c);
template<typename T, size_t N> char (& array_size(T(&)[N]) )[N];
void pass(int *q) {
int n1 = sizeof(q); // oops, size of the pointer!
int n2 = sizeof array_size(q); // error! q is not an array!
}
int main() {
int p[] = { 1, 2, 3, 4, 5, 6 };
int u[sizeof array_size(p)]; // we get the size at compile time.
pass(p);
}