【问题标题】:What is better implicit conversion through constructor or explicit function in this case?在这种情况下,通过构造函数或显式函数进行更好的隐式转换是什么?
【发布时间】:2019-02-25 02:59:59
【问题描述】:

我正在使用 C++ 为 String 创建自己的课程,仅用于学习目的。

我坚持我应该做出决定的地方。让我解释一下。

我的班级有两个选择。我将只在下面发布相关的代码片段,因为我不想分散您对手头问题的注意力。如果为了帮助我,您需要更多信息,我很乐意提供。

选项 1

class String {
    size_t _length;
    char* _stringHead;
public:
    String(const std::string&);
    String(const char*);
    String(const char);
};
String operator+(String, const String);

const bool operator==(const String, const String);
const bool operator!=(const String, const String);
const bool operator<(const String, const String);
const bool operator<=(const String, const String);
const bool operator>(const String, const String);
const bool operator>=(const String, const String);

选项 2

class String {
    size_t _length;
    char* _stringHead;
public:
    //irrelevant part of code in Option 2
    String(const std::string&);
    String(const char*);
    String(const char);
    //irrelevant part of code in Option 2
};
String operator+(String, const String&);

const bool operator==(const String&, const String&);
const bool operator!=(const String&, const String&);
const bool operator<(const String&, const String&);
const bool operator<=(const String&, const String&);
const bool operator>(const String&, const String&);
const bool operator>=(const String&, const String&);

//for std::string
String operator+(String, const std::string&);

const bool operator==(const String&, const std::string&);
const bool operator!=(const String&, const std::string&);
const bool operator<(const String&, const std::string&);
const bool operator<=(const String&, const std::string&);
const bool operator>(const String&, const std::string&);
const bool operator>=(const String&, const std::string&);

String operator+(const std::string&, String);

const bool operator==(const std::string&, const String&);
const bool operator!=(const std::string&, const String&);
const bool operator<(const std::string&, const String&);
const bool operator<=(const std::string&, const String&);
const bool operator>(const std::string&, const String&);
const bool operator>=(const std::string&, const String&);
//for std::string

//the same goes for char* and char
...
//the same goes for char* and char

所以,从 Option 1Option 2 规范中可以看出,这里的决定是关于是否使用在构造函数的帮助下完成的隐式类型转换或者为我希望 String 类型使用的每种类型分别键入每个实用程序。

据我目前所见,使用第一种方法的好处是更容易实现和维护。而第二种方法可能会产生更好的性能结果。

我想获得建设性的论点,哪种方法更好,在什么情况下,你会使用哪种方法。我认为我在这里最感兴趣的部分是第二种方法的性能优势是否合理。

【问题讨论】:

  • 由于这仅用于学习目的,因此双向进行并运行一些性能测试(计时)以查看哪个性能更好。
  • @hellohowdoyoudo,同意,您必须在循环中进行 1000 次迭代。有时我这样做只是为了满足自己的好奇心。如果差异可以忽略不计,我会坚持使用更简单的实现。
  • const bool operator==(const String, const String); 创建任意长度的 String 参数的副本。比较见std::string::operator+en.cppreference.com/w/cpp/string/basic_string/operator%2B
  • 请记住,任何采用单个参数的构造函数都可能被编译器捕获并用于将对象静默转换为另一种类型。当您尝试将错误类型的参数传递给函数时,通常会发生这种情况。隐式转换的存在会使发现错误变得相当困难。无论如何,这不是一个确定的论点,但它(在我看来)是一个强有力的论据,可以让你的所有“转换”构造函数explicit
  • @hellohowdoyoudo 我推荐Quick C++ Benchmark 进行微基准测试。它可以测量非常短的运行时间。例如,请参阅quick-bench.com/LXeHNjtCLFtB-Ld1A_tLK9J3cKo,其中 小字符串优化 的效果是针对单个对象的创建/销毁来衡量的。

标签: c++ class constructor header explicit


【解决方案1】:

在将参数类型的实例传递给期望类类型的方法时,使用隐式构造函数创建类类型的实例。这种隐式转换是通过调用类的构造函数来完成的。

例如,运行与您的类似的代码:

#include <iostream>

class String {
 public:
  String(const std::string& s) {
    std::cout << "called" << std::endl;
  };
};

std::ostream& operator<< (std::ostream& stream, const String& s) {
  return stream;
}

void hello(String s) {
  std::cout << "Hello " << s; // Outputs "called" before "Hello ".
}

int main() {
  std::string s = "world";
  hello(s); // Uses the implicit conversion constructor.
}

因为每次都必须创建类String 的新实例,所以预计性能会略有下降。但是,在我看来,这还不足以超过好处:隐式转换可以大大简化类设计者的工作,并使使用类更容易。

但是,请记住,在某些情况下,如果转换自动发生,团队成员更有可能感到惊讶,而不是因为转换的存在而得到帮助。

这是一个例子:

#include <iostream>

class String {
 public:
  String(int size) {};
};

std::ostream& operator<< (std::ostream& stream, const String& s) {
    return stream;
}

void hello(String s) {
  std::cout << "Hello " << s; // Prints "Hello " as no error occurs.
}

int main() {
  hello(10); // It still calls the implicit conversion constructor.
}

在上面的代码中,explicit 关键字产生的错误消息可以节省一些调试时间。

隐式转换有意义的一些情况是:

  1. 该类的构造足够便宜,您不必关心它是否是隐式构造的。
  2. 某些类在概念上与其参数相似,例如 std::string 反映了与它可以隐式转换的 const char* 相同的概念,因此隐式转换是有意义的,就像这里的情况一样。李>
  3. 如果禁用隐式转换,某些类使用起来会变得更加不愉快。想想每次要传递字符串文字时都必须显式构造 std::string

隐式转换不太有意义的一些情况是:

  1. 建设成本​​高。
  2. 类在概念上与它们的参数非常不同。考虑Stringint 的示例。
  3. 构造可能会产生不希望的副作用。例如,AnsiString 类不应隐式从 UnicodeString 构造,因为 Unicode 到 ANSI 的转换可能会丢失信息。

因此,在您的特定情况下,我的建议是使用转换,因为它是有意义的,因为您的类与 std::string 非常相似,并尽量减少代码重复,但在未来,使用隐式转换与思考

【讨论】:

    猜你喜欢
    • 2018-10-29
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多