C++;结构替换C结构 与我团队中的新开发人员讨论后,我意识到C++中仍然存在使用C结构的习惯,因为它们应该更好(即更快、更精简、更美观、更适合您的理由)。 哪些值得分享的例子,显示了C构造,与类似C++构造相比?>/P> 对于每个例子,我需要阅读C++构造与原C构造一样好或更好的原因。目的是提供一些C++构造的替代方案,这些C++构造在C++代码中被认为有危险性/不安全性(C++ + 0x有效的,只有被明确地标记为c++0x的唯一答案才被接受)。

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++中简单的数据聚合。由于数

我将在下面发布一个答案(结构内联初始化)作为示例

注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成员,并希望它在默认情况下为“零”,那么我们仍然需要更改每个内联成员
下面的代码将内联构造函数(如果真的有用),因此成本为零(如上面的C代码所示):

(好处是我们可以添加一个操作符==方法,但这个好处不属于主题,因此值得一提,但不值得作为答案。)

编辑:C99已初始化名称 亚当·罗森菲尔德(Adam Rosenfield)发表了一个有趣的评论,我觉得非常有趣:


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定义中的语法错误将指向正确的位置,而不是调用宏的位置
  • 可以调试成函数
默认参数: C: C++替换/等价: 为什么这是一个进步: 允许程序员以更少的源代码行和更紧凑的形式编写和表达程序的功能。还允许将未使用参数的默认值表示为最接近实际使用位置。对于调用方,简化了类/结构的接口

RAII和所有随后的荣耀与手动资源获取/发布 在C中:

例如,
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);
      }