代码之家  ›  专栏  ›  技术社区  ›  Stormenet

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

  •  802
  • Stormenet  · 技术社区  · 16 年前

    作为一个 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 回复  |  直到 7 年前
        1
  •  1080
  •   Azeem Rob Hyndman    7 年前

    C++ 11:是的!

    C++ 11和向前具有相同的特征(称为 delegating constructors )

    语法与C稍有不同:

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

    C++ 03:没有

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

    1. 可以通过默认参数组合两个(或多个)构造函数:

      class Foo {
      public:
        Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
        // ...
      };
      
    2. 使用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 供参考。

        2
  •  107
  •   mskfisher KeithS    13 年前

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

    这在C++ 11中改变了(AKC+0x),它增加了对以下语法的支持:
    (示例取自 Wikipedia )

    class SomeType
    {
      int number;
    
    public:
      SomeType(int newNumber) : number(newNumber) {}
      SomeType() : SomeType(42) {}
    };
    
        3
  •  40
  •   danday74    7 年前

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

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

    裁判: https://isocpp.org/wiki/faq/ctors#init-methods

        4
  •  20
  •   Azeem Rob Hyndman    7 年前

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

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

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

        5
  •  17
  •   Peter Mortensen icecrime    10 年前

    C++11 A 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) {}
    };
    

    这样就不需要创建初始化助手方法。建议不要在构造函数或析构函数中调用任何虚拟函数,以避免使用任何可能未初始化的成员。

        6
  •  11
  •   lyngvi    10 年前

    如果你想成为恶魔,你可以使用“新”操作符:

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

    似乎对我有用。

    编辑

    正如@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 */
        } 
    };
    

    当然,看起来不那么优雅。@约翰尼多的解决方案要好得多。

        7
  •  8
  •   unwind    16 年前

    不,在C++中,你不能从构造函数调用构造函数。正如沃伦指出的,你能做的是:

    • 使用不同的签名重载构造函数
    • 对参数使用默认值,以使“简单”版本可用

    注意,在第一种情况下,不能通过从另一个构造函数调用一个构造函数来减少代码重复。当然,您可以有一个独立的、私有的/受保护的方法来完成所有初始化,并让构造函数主要处理参数处理。

        8
  •  5
  •   izogfif    13 年前

    在VisualC++中,你也可以在构造函数中使用这个符号:-& Gt;ClordNo::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中测试过。你也可以打电话 几个 我想,这样的构造函数就像Java和C语言一样。

    旁白:坦率地说,我很惊讶之前没有提到这一点。

        9
  •  3
  •   James Eichele Bernard Igiri    13 年前

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

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

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

        10
  •  2
  •   Peter Mortensen icecrime    10 年前

    我建议使用 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++中是不允许的。因此,我们可以定义一个私有的friend方法,它实现第一个构造函数应该做的事情:

    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();
    }
    

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

        11
  •  2
  •   V15I0N    9 年前

    这种方法可能适用于某些类型的类(当赋值运算符的行为“良好”时):

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

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

    如果这就是你要找的,那就不可能了。

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

    甚至可以在末尾有一个带有默认参数的构造函数。

    但是您可能没有多个构造函数,然后分别调用它们中的每一个。

        13
  •  1
  •   Peter Mortensen icecrime    10 年前

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

        14
  •  1
  •   Scott Smith    7 年前

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

    C++ 11介绍 delegating constructors :

    委托建造师

    如果类本身的名称作为类或标识符出现在 成员初始值设定项列表,则列表必须由该成员组成 仅限初始值设定项;此类构造函数称为委托 构造函数,以及由 初始值设定项列表是目标构造函数

    在这种情况下,目标构造函数是通过重载选择的。 解析并首先执行,然后控件返回到 执行委托构造函数及其主体。

    委派构造函数不能递归。

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

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

        15
  •  0
  •   gyula    8 年前

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

    #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标准进行编译: G++MIN .CPP-STD= C++ 98 -O Test1

    你会看到:

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

    所以:)