【问题标题】:Default Constructors all encapsulated in the header files默认构造函数全部封装在头文件中
【发布时间】:2011-10-05 03:58:31
【问题描述】:

今天在我的课上,我们进行了继承练习。我们要编写一个继承自 Student 类的 UnderGrad 类,该类继承自 Person 类。

Person 类有 3 个变量:姓名、地址和生日。

学生有一个 ID 号、专业和学位类型。

最后,本科生有以前的高中和学分获得变量。

当我为每个类编写头文件时,默认构造函数如下所示:

   //snippet from UnderGrad.h
 Undergrad(string nm = "", string add = "", string bday = "", int id = 0, string maj = "", string degtyp = "", string hs = "", int cred = 0) : Student(nm,add,bday,id,maj,degtyp) {

     highSchool = hs;
     credits = cred;
};


//snippet from Student.h
Student(string nm = "", string add = "", string bday = "", int id = 0, string maj = "", string degtyp = "") : Person(nm,add,bday){
     stuId = id;
     major = maj;
     degreeType = degtyp;
};

//snippet from Person.h
Person(string nm = "", string add = "", string bday = ""){
     name = nm;
     address = add;
     bDay = bday;
};

我的教授说这是不可读的,而且它不是真的这样做的。

我想知道,以这种方式创建默认构造函数有什么问题?有什么问题吗?有没有更好的方法?

【问题讨论】:

  • 你应该问你的教授他们对这部分的哪一部分有问题。
  • 代码一目了然!我认为没有什么可抱怨的。
  • 这段代码中唯一不清楚的是使用嵌套继承和非抽象非叶类的基本原理。这似乎不太可能有助于解决任何实际的编程问题。
  • 叶类是位于继承树底部的类。也就是说,没有其他类继承自的类。

标签: c++ constructor


【解决方案1】:

如果没有代码上下文的好处,我只会对这些构造函数进行一些更改。

  1. 我会考虑将构造函数的定义移动到单独的源文件中。这将允许更改定义而不会触发其余代码的重新编译。对于小程序,或者非常简单的构造函数,它们不太可能在不更改标头的情况下更改,这可能不值得。

  2. 我将更改构造函数的定义以使用成员初始化程序列表,而不是在构造函数主体中分配成员。 (有关解释,请参阅 Als 的答案)。

  3. 我会删除函数定义末尾的分号,它们完全不需要。

  4. 我会将长构造函数分成几行,以避免代码行过长(长行可能不适合您的编辑器,并且可能难以执行差异和合并)。

  5. 您的头文件中似乎有using namespace ::stdusing ::std::string。这是不好的做法,因为这意味着包含您的标头的代码将应用此using。这可能会使客户端代码的含义发生变化,或者使客户端代码变得模棱两可而无法编译。我会删除using,而是使用std::string 的限定名称。


这些变化付诸实施:

//snippet from UnderGrad.h
Undergrad(
    std::string nm = "",
    std::string add = "",
    std::string bday = "",
    int id = 0,
    std::string maj = "",
    std::string degtyp = "",
    std::string hs = "",
    int cred = 0) :
        Student(nm,add,bday,id,maj,degtyp),
        highSchool(hs),
        credits(cred)
{
}

//snippet from Student.h
Student(
    std::string nm = "",
    std::string add = "",
    std::string bday = "",
    int id = 0,
    std::string maj = "",
    std::string degtyp = "") :
      Person(nm,add,bday),
      stuId(id),
      major(maj),
      degreeType(degtyp)
{
}

//snippet from Person.h
Person(std::string nm = "", std::string add = "", std::string bday = "") :
    name(nm), address(add), bDay(bday)
{
}

【讨论】:

  • 这里顺便提一下,除了#2,其他的可能看个人的选择或风格,但不遵循#2真的会影响性能,应该一直遵循。跨度>
  • 我不知道它是否会真的影响性能 - 每个构造都会执行更多指令,但与缓冲区分配相比,这些指令可能会相形见绌在string 的构造函数中,所以我怀疑它是否会引起注意。
  • #1 对于小型项目可能是无害的。 #2 可能不会成为瓶颈,但对于可复制但不可分配的类型是必要的。 #3 是完全无害的(尽管分号可能不合法?)。 #4 是个人风格问题,实际上取决于您的开发环境。 #5 风格很差,但如果它真的导致问题,它很容易修复。基本上这些都是挑剔的,但在任何情况下它们都可能会有所帮助。
  • 这段代码更大的问题是对所有内容都使用字符串(取决于代码的使用方式),以及不必要的继承使用。对 bDay 使用 Date 类可能会更好(例如),但它确实取决于预期用途。这段代码唯一真正糟糕的部分可能是嵌套继承和非抽象非叶类,但同样,如果没有比问题中提供的更多代码,这是不可能评论的。
  • 我们最终会使用一个结构来处理大多数事情,比如日期、地址和生日。整个事情的重点只是为了说明继承。该程序不会做任何实际的事情。现在,程序唯一要做的就是打印出所有变量是什么。
【解决方案2】:

语义上很好,尽管您可以为成员字符串使用初始化器而不是分配每个字符串。

从风格上讲,你的台词有点长,但这是我能认真考虑的全部。

【讨论】:

    猜你喜欢
    • 2011-09-03
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 2010-10-30
    相关资源
    最近更新 更多