为什么我可以在C ++中定义函数内的结构和类?

时间:2009-05-18 02:43:35

标签: c++ data-structures functional-programming

我只是在C ++中错误地做了类似的事情,并且它有效。我为什么要这样做?

int main(int argc, char** argv) {
    struct MyStruct
    {
      int somevalue;
    };

    MyStruct s;
    s.somevalue = 5;
}

现在这样做之后,我记得在很久以前的某个地方读过这个技巧,作为一种穷人的C ++函数式编程工具,但我不记得为什么这个有效,或者我在哪里阅读。

欢迎回答任何一个问题!

注意:虽然在撰写问题时我没有得到this question的任何引用,但是当前的侧栏指出了它,所以我会把它放在这里供参考,无论哪种方式问题都不同但可能很有用。

6 个答案:

答案 0 :(得分:67)

[编辑18/4/2013]:令人高兴的是,下面提到的限制已经在C ++ 11中解除了,所以本地定义的类毕竟是有用的!感谢评论员bamboon。

本地定义类的能力创建自定义仿函数(具有operator()()的类,例如用于传递给std::sort()或“循环体”的比较函数std::for_each()}更方便。

不幸的是,C ++禁止使用带有模板的本地定义的类,因为它们没有链接。由于仿函数的大多数应用程序都涉及在仿函数类型上模板化的模板类型,因此本地定义的类不能用于此 - 您必须在函数外部定义它们。 :(

[EDIT 1/11/2009]

标准的相关引用是:

  

14.3.1 / 2:。本地类型,没有链接的类型,未命名的类型或从这些类型中复合的类型不得用作模板参数模板类型参数。

答案 1 :(得分:28)

本地定义的C ++类的一个应用是Factory design pattern


// In some header
class Base
{
public:
    virtual ~Base() {}
    virtual void DoStuff() = 0;
};

Base* CreateBase( const Param& );

// in some .cpp file
Base* CreateBase( const Params& p )
{
    struct Impl: Base
    {
        virtual void DoStuff() { ... }
    };

    ...
    return new Impl;
}

虽然你可以对匿名命名空间做同样的事情。

答案 2 :(得分:9)

嗯,基本上,为什么不呢? C中的struct(可以追溯到时间的早期)只是一种声明记录结构的方法。如果你想要一个,为什么不能在声明一个简单变量的地方声明呢?

一旦你这样做,那么请记住,如果可能的话,C ++的目标是与C兼容。所以它保持不变。

答案 3 :(得分:5)

例如,http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html中的“7.8:本地类:函数内部”部分提到了它,它将其称为“本地类”,并称它“在涉及继承或模板的高级应用程序中非常有用” ”

答案 4 :(得分:5)

这对于进行一些基于堆栈的异常安全工作实际上非常有用。或者从具有多个返回点的函数进行一般清理。这通常被称为RAII(资源获取是初始化)成语。

void function()
{

    struct Cleaner
    {
        Cleaner()
        {
            // do some initialization code in here
            // maybe start some transaction, or acquire a mutex or something
        }

        ~Cleaner()
        {
             // do the associated cleanup
             // (commit your transaction, release your mutex, etc.)
        }
    };

    Cleaner cleaner();

    // Now do something really dangerous
    // But you know that even in the case of an uncaught exception, 
    // ~Cleaner will be called.

    // Or alternatively, write some ill-advised code with multiple return points here.
    // No matter where you return from the function ~Cleaner will be called.
}

答案 5 :(得分:2)

用于制作正确初始化的对象数组。

我有一个没有默认构造函数的C类。我想要一个C类对象的数组。我想出了我希望这些对象是如何初始化的,然后使用静态方法从C派生出一个D类,该方法为D的默认构造函数中的C提供参数:

#include <iostream>
using namespace std;

class C {
public:
  C(int x) : mData(x)  {}
  int method() { return mData; }
  // ...
private:
  int mData;
};

void f() {

  // Here I am in f.  I need an array of 50 C objects starting with C(22)

  class D : public C {
  public:
    D() : C(D::clicker()) {}
  private:
    // I want my C objects to be initialized with consecutive
    // integers, starting at 22.
    static int clicker() { 
      static int current = 22;
      return current++;
    } 
  };

  D array[50] ;

  // Now I will display the object in position 11 to verify it got initialized
  // with the right value.  

  cout << "This should be 33: --> " << array[11].method() << endl;

  cout << "sizodf(C): " << sizeof(C) << endl;
  cout << "sizeof(D): " << sizeof(D) << endl;

  return;

}

int main(int, char **) {
  f();
  return 0;
}

为简单起见,此示例使用了一个简单的非默认构造函数以及在编译时已知值的情况。可以直接将此技术扩展到您希望使用仅在运行时知道的值初始化的对象数组的情况。