首页 文章

我可以从C中的另一个构造函数(做构造函数链接)调用构造函数吗?

提问于
浏览
787

作为一个C#开发人员,我习惯于运行构造函数:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

有没有办法在C中这样做?

我尝试调用类名并使用'this'关键字,但都失败了 .

15 回答

  • 1064

    在调用构造函数时,它实际上是从堆栈或堆中分配内存 . 因此,在另一个构造函数中调用构造函数会创建一个本地副本 . 所以我们正在修改另一个对象,而不是我们关注的对象 .

  • 40

    值得指出的是,您可以在构造函数中调用父类的构造函数,例如:

    class A { /* ... */ };
    
    class B : public A
    {
        B() : A()
        {
            // ...
        }
    };
    

    但是,不,你不能调用同一个类的另一个构造函数 .

  • 1

    不,你不能在C 03中调用另一个构造函数(称为委托构造函数) .

    这在C 11(又名C 0x)中发生了变化,增加了对以下语法的支持:
    (例子来自Wikipedia

    class SomeType
    {
      int number;
    
    public:
      SomeType(int newNumber) : number(newNumber) {}
      SomeType() : SomeType(42) {}
    };
    
  • 7

    我相信你可以从构造函数中调用构造函数 . 它将编译并运行 . 我最近看到有人这样做,它在Windows和Linux上运行 .

    它只是没有做你想要的 . 内部构造函数将构造一个临时本地对象,一旦外部构造函数返回,该对象就会被删除 . 它们也必须是不同的构造函数,否则您将创建递归调用 .

    参考:https://isocpp.org/wiki/faq/ctors#init-methods

  • 2

    不,在C中,您无法从构造函数中调用构造函数 . 沃伦指出,你可以做的是:

    • 使用不同的签名重载构造函数

    • 在参数上使用默认值,以使"simpler"版本可用

    请注意,在第一种情况下,您无法通过从另一个构造函数调用来减少代码重复 . 您当然可以使用单独的private / protected方法来执行所有初始化,并让构造函数主要处理参数处理 .

  • 5

    比测定更容易测试:)试试这个:

    #include <iostream>
    
    class A {
    public:
        A( int a) : m_a(a) {
            std::cout << "A::Ctor" << std::endl;    
        }
        ~A() {
            std::cout << "A::dtor" << std::endl;    
        }
    public:
        int m_a;
    };
    
    class B : public A {
    public:
        B( int a, int b) : m_b(b), A(a) {}
    public:
        int m_b;
    };
    
    int main() {
        B b(9, 6);
        std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
        return 0;
    }
    

    并用98 std编译它:g main.cpp -std = c 98 -o test_1

    你会看见:

    A::Ctor
    Test constructor delegation a = 9; b = 6
    A::dtor
    

    所以:)

  • 2

    C++11: Yes!

    C 11及以后版本具有相同的功能(称为delegating constructors) .

    语法与C#略有不同:

    class Foo {
    public: 
      Foo(char x, int y) {}
      Foo(int y) : Foo('a', y) {}
    };
    

    C++03: No

    不幸的是,在C 03中没有办法做到这一点,但有两种模拟方法:

    • 您可以通过默认参数组合两个(或更多)构造函数:
    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
    • 使用init方法共享公共代码:
    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

    请参阅the C++FAQ entry以供参考 .

  • 103

    另一个尚未显示的选项是将您的类拆分为两个,在原始类周围包装一个轻量级接口类,以实现您正在寻找的效果:

    class Test_Base {
        public Test_Base() {
            DoSomething();
        }
    };
    
    class Test : public Test_Base {
        public Test() : Test_Base() {
        }
    
        public Test(int count) : Test_Base() {
            DoSomethingWithCount(count);
        }
    };
    

    如果你有许多构造函数必须调用它们的“下一级”对应物,这可能会变得混乱,但对于少数构造函数,它应该是可行的 .

  • 17

    如果你想成为邪恶的,你可以使用就地“新”运算符:

    class Foo() {
        Foo() { /* default constructor deliciousness */ }
        Foo(Bar myParam) {
          new (this) Foo();
          /* bar your param all night long */
        } 
    };
    

    似乎适合我 .

    edit

    正如@ElvedinHamzagic指出的那样,如果Foo包含一个分配内存的对象,那么该对象可能不会被释放 . 这使事情进一步复杂化 .

    一个更一般的例子:

    class Foo() {
    private:
      std::vector<int> Stuff;
    public:
        Foo()
          : Stuff(42)
        {
          /* default constructor deliciousness */
        }
    
        Foo(Bar myParam)
        {
          this->~Foo();
          new (this) Foo();
          /* bar your param all night long */
        } 
    };
    

    看起来有点不那么优雅,当然 . @ JohnIdol的解决方案要好得多 .

  • 2

    这种方法可能适用于某些类(当赋值运算符表现'井'时):

    Foo::Foo()
    {
        // do what every Foo is needing
        ...
    }
    
    Foo::Foo(char x)
    {
        *this = Foo();
    
        // do the special things for a Foo with char
        ...
    }
    
  • 1

    C++11中,constructor can call another constructor overload

    class Foo  {
         int d;         
    public:
        Foo  (int i) : d(i) {}
        Foo  () : Foo(42) {} //New to C++11
    };
    

    此外,成员也可以像这样初始化 .

    class Foo  {
         int d = 5;         
    public:
        Foo  (int i) : d(i) {}
    };
    

    这应该消除了创建初始化辅助方法的需要 . 并且仍然建议不要在构造函数或析构函数中调用任何虚函数,以避免使用任何可能未初始化的成员 .

  • 11

    我建议使用 private friend 方法来实现构造函数的应用程序逻辑,并由各种构造函数调用 . 这是一个例子:

    假设我们有一个名为 StreamArrayReader 的类,其中包含一些私有字段:

    private:
        istream * in;
          // More private fields
    

    我们想要定义两个构造函数:

    public:
        StreamArrayReader(istream * in_stream);
        StreamArrayReader(char * filepath);
        // More constructors...
    

    第二个只使用第一个(当然我们不想复制前者的实现) . 理想情况下,人们想做的事情如下:

    StreamArrayReader::StreamArrayReader(istream * in_stream){
        // Implementation
    }
    
    StreamArrayReader::StreamArrayReader(char * filepath) {
        ifstream instream;
        instream.open(filepath);
        StreamArrayReader(&instream);
        instream.close();
    }
    

    但是,C中不允许这样做 . 出于这个原因,我们可以定义一个私有方法如下,它实现了第一个构造函数应该做的事情:

    private:
      friend void init_stream_array_reader(StreamArrayReader *o, istream * is);
    

    现在这个方法(因为它是朋友)可以访问 o 的私有字段 . 然后,第一个构造函数变为:

    StreamArrayReader::StreamArrayReader(istream * is) {
        init_stream_array_reader(this, is);
    }
    

    请注意,这不会为新创建的副本创建多个副本 . 第二个成为:

    StreamArrayReader::StreamArrayReader(char * filepath) {
        ifstream instream;
        instream.open(filepath);
        init_stream_array_reader(this, &instream);
        instream.close();
    }
    

    也就是说,不是让一个构造函数调用另一个,而是调用私人朋友!

  • 0

    简单地说,你不能在C 11之前 .

    C 11介绍delegating constructors

    委托构造函数如果类的名称本身在成员初始值设定项列表中显示为class-or-identifier,则列表必须仅包含该成员初始值设定项;这样的构造函数称为委托构造函数,由初始化程序列表的唯一成员选择的构造函数是目标构造函数 . 在这种情况下,目标构造函数通过重载决策选择并首先执行,然后执行control返回委托构造函数并执行其正文 . 委托构造函数不能递归 . class Foo {
    上市:
    Foo(char x,int y){}
    Foo(int y):Foo('a',y){} // Foo(int)委托给Foo(char,int)
    };

    请注意,委托构造函数是一个全有或全无的提议;如果构造函数委托给另一个构造函数,则不允许调用构造函数在其初始化列表中包含任何其他成员 . 如果您考虑初始化const / reference成员一次,并且只考虑一次,这是有道理的 .

  • 20

    如果我正确理解你的问题,你会问你是否可以在C中调用多个构造函数?

    如果这就是你要找的东西,那么不 - 那是不可能的 .

    您当然可以拥有多个构造函数,每个构造函数都具有唯一的参数签名,然后在实例化新对象时调用所需的构造函数 .

    你甚至可以在最后有一个带有默认参数的构造函数 .

    但是你可能没有多个构造函数,然后分别调用它们 .

  • 2

    在Visual C中,您还可以在构造函数中使用此表示法:this-> Classname :: Classname(另一个构造函数的参数) . 请参阅以下示例:

    class Vertex
    {
     private:
      int x, y;
     public:
      Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
      Vertex()
      {
       this->Vertex::Vertex(-1, -1);
      }
    };
    

    我不知道它是否在其他地方工作,我只在Visual C 2003和2008中测试过它 . 你也可以这样调用 several 构造函数,我想,就像在Java和C#中一样 .

    P.S . :坦率地说,我很惊讶这是前面没有提到的 .

相关问题