Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在构造函数中做什么(不做) 我想问你关于C++中构造函数的最佳实践。我不太确定在构造函数中应该做什么,不应该做什么_C++_Oop_Constructor_Shared Ptr - Fatal编程技术网

在构造函数中做什么(不做) 我想问你关于C++中构造函数的最佳实践。我不太确定在构造函数中应该做什么,不应该做什么

在构造函数中做什么(不做) 我想问你关于C++中构造函数的最佳实践。我不太确定在构造函数中应该做什么,不应该做什么,c++,oop,constructor,shared-ptr,C++,Oop,Constructor,Shared Ptr,我应该只将其用于属性初始化、调用父构造函数等吗。? 或者我甚至可以将更复杂的函数放入其中,比如读取和解析配置数据,设置外部库a.s.o 还是应该为此编写特殊函数?分别初始化()/清理() 这里的赞成者和反对者是什么 例如,我发现我可以在使用init()和cleanup()时去掉共享指针。我可以在堆栈上创建对象作为类属性,并在构建之后初始化它 如果我在构造函数中处理它,我需要在运行时实例化它。那我需要一个指针 我真的不知道该怎么决定 也许你能帮我个忙?好吧,«构造器»来自建筑、建造、安装。这就是所

我应该只将其用于属性初始化、调用父构造函数等吗。? 或者我甚至可以将更复杂的函数放入其中,比如读取和解析配置数据,设置外部库a.s.o

还是应该为此编写特殊函数?分别<代码>初始化()/
清理()

这里的赞成者和反对者是什么

例如,我发现我可以在使用
init()
cleanup()
时去掉共享指针。我可以在堆栈上创建对象作为类属性,并在构建之后初始化它

如果我在构造函数中处理它,我需要在运行时实例化它。那我需要一个指针

我真的不知道该怎么决定


也许你能帮我个忙?

好吧,«构造器»来自建筑、建造、安装。这就是所有初始化发生的地方。每当您实例一个类时,请使用构造函数来确保所有操作都已完成,以便使新对象可以使用。

构造函数需要创建一个可以从go这个词开始使用的对象。如果由于某种原因无法创建可用对象,它应该抛出一个异常并处理它。因此,应该从构造函数中调用对象正常工作所需的所有补充方法/函数(除非您希望具有类似延迟加载的特性)


  • 不要调用
    删除此
    或构造函数中的析构函数
  • 不要使用init()/cleanup()成员。如果每次创建实例时都必须调用init(),那么init()中的所有内容都应该在构造函数中。构造函数的作用是将实例置于一致状态,从而允许使用定义良好的行为调用任何公共成员。类似地,对于cleanup(),加上cleanup()将终止。(但是,当您有多个构造函数时,使用由它们调用的私有init()函数通常很有用。)
  • 在构造函数中做更复杂的事情是可以的,这取决于类的预期用途和总体设计。例如,在某种整数或点类的构造函数中读取文件不是一个好主意;用户希望这些产品的制造成本低廉。同样重要的是要考虑文件访问构造函数将如何影响编写单元测试的能力。最好的解决方案通常是使用一个构造函数,该构造函数只获取构造成员所需的数据,并编写一个非成员函数来执行文件解析并返回一个实例

构造函数和析构函数中最常见的错误是使用多态性多态性通常在构造函数中不起作用

e、 g:

这是因为对象B在执行母类A的构造函数时尚未构造。。。因此,它不可能调用
void doA()的重写版本


多态性将在构造函数中起作用的示例:

class A
{
public: 
    void callAPolymorphicBehaviour()
    {
        doOverridenBehaviour(); 
    }

    virtual void doOverridenBehaviour()
    {
        doA();
    }

    void doA(){}
};

class B : public A
{
public:
    B()
    {
        callAPolymorphicBehaviour();
    }

    virtual void doOverridenBehaviour()
    {
        doB()
    }

    void doB(){}
};

void testB()
{
   B b; // this WILL call doB();
}

这一次,背后的原因是:在调用
virtual
函数
doOverridenBehaviour()
时,对象b已经初始化(但尚未构造),这意味着它的虚拟表已经初始化,因此可以执行多态性。

复杂的逻辑和构造函数并不总是很好地混合,而且有强烈的支持者反对在一个建造商中做繁重的工作(有理由)

基本规则是构造函数应该产生一个完全可用的对象

class Vector
{
public:
  Vector(): mSize(10), mData(new int[mSize]) {}
private:
  size_t mSize;
  int mData[];
};
这并不意味着一个完全初始化的对象,您可以推迟一些初始化(认为是懒惰的),只要用户不必考虑它

class Vector
{
public:
  Vector(): mSize(0), mData(0) {}

  // first call to access element should grab memory

private:
  size_t mSize;
  int mData[];
};
如果有繁重的工作要做,您可以选择继续使用生成器方法,该方法将在调用构造函数之前完成繁重的工作。例如,想象一下从数据库检索设置并构建一个设置对象

// in the constructor
Setting::Setting()
{
  // connect
  // retrieve settings
  // close connection (wait, you used RAII right ?)
  // initialize object
}

// Builder method
Setting Setting::Build()
{
  // connect
  // retrieve settings

  Setting setting;
  // initialize object
  return setting;
}
如果延迟对象的构造会产生显著的好处,则此生成器方法非常有用。例如,如果对象占用大量内存,那么在可能失败的任务之后推迟内存获取可能不是一个坏主意

此生成器方法表示私有构造函数和公共(或朋友)生成器。请注意,拥有私有构造函数会对类的用法施加许多限制(例如,不能存储在STL容器中),因此可能需要在其他模式中合并。这就是为什么这种方法只能在特殊情况下使用

你可能想考虑如何测试这样的实体,如果你依赖一个外部的事物(文件/db),思考依赖注入,它确实有助于单元测试。

< P>我宁愿问:

What all to do in the constructor?
上面没有提到的就是OP问题的答案

我认为构造函数的唯一目的是

  • 将所有成员变量初始化为已知状态,以及

  • 分配资源(如适用)


  • 第#1项听起来很简单,但我发现它经常被遗忘/忽略,只有通过静态分析工具才能提醒。永远不要低估这一点(双关语)

    简单的回答:这要看情况而定

    在设计软件时,您可能希望通过以下原则进行编程(“资源获取是初始化”)。这意味着(除其他外)对象本身负责其资源,而不是调用方。此外,您可能希望熟悉(在不同程度上)

    例如,考虑:

    void func() { MyFile f("myfile.dat"); doSomething(f); } void func(){ MyFile f(“MyFile.dat”); 剂量测定法(f); } 如果您以这样的方式设计类
    MyFile
    ,那么您可以 void func() { MyFile f("myfile.dat"); doSomething(f); }