标签: effective-c++

"避免将句柄返回到对象内部",那么替代方案是什么?

Scott Meyers的有效C++在第5章第28项中告诉我们避免将"句柄"(指针,引用或迭代器)返回到对象内部,这绝对是一个好点.

即不要这样做:

class Family
{
public:
    Mother& GetMother() const;
}
Run Code Online (Sandbox Code Playgroud)

因为它破坏了封装并允许改变私有对象成员.

甚至不这样做:

class Family
{
public:
    const Mother& GetMother() const;
}
Run Code Online (Sandbox Code Playgroud)

因为它可以导致"悬空手柄",这意味着您保留对已经销毁的对象的成员的引用.

现在,我的问题是,有什么好的选择吗?想象一下妈妈很沉重!如果我现在返回母亲的副本而不是参考,GetMother正在成为一个相当昂贵的操作.

你如何处理这种情况?

c++ effective-c++

13
推荐指数
3
解决办法
1979
查看次数

前向声明包括,声明包括(ClassFwd.h + Class.h)

在Effective C++(第3版)中,Scott Meyers在第31项中建议,除了经典的声明(.h)和定义(.cpp)文件之外,类应该具有前向声明包含文件(fwd.h),哪个类不需要完整定义可以使用,而不是向前声明自己.

我有点看到它的情况,但我真的不认为这是一个可行的选择...它似乎很难维持,相当矫枉过正,几乎没有必要.

但是,我可以看到它用于模板前向声明,它相当重.但对于简单的课程?这似乎很难维护,并且会创建一大堆几乎空的包含文件,这些文件只是用于非常小的目的......值得麻烦吗?

这是一个例子:

// Class.h
class Class
{
    Class();
    ~Class();
};

// ClassFwd.h
class Class;

// Class.cpp
Class::Class()
{
}

Class::~Class()
{
}
Run Code Online (Sandbox Code Playgroud)

我的问题:

你们有什么感想?如果这是一个很好的做法?

注意我最感兴趣的是这个练习的论据,看看我是否错过了一些让我同意Scott Meyers的内容.

c++ include forward-declaration effective-c++

11
推荐指数
1
解决办法
2533
查看次数

在成员初始化列表中填写std :: array

以下代码有效,但我想避免警告:

警告:'fitness :: vect_'应该在成员初始化列表中初始化[-Weffc ++]

当用g++ -Weffc++开关编译时:

#include <array>

template<class T, unsigned N>
class fitness
{
public:
  explicit fitness(T v)
  {
    static_assert(N, "fitness zero length");

    vect_.fill(v);
  }

private:
  std::array<T, N> vect_;
};

int main()
{
  fitness<double, 4> f(-1000.0);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我应该忽略这个警告吗?有没有办法填写vect_构造函数初始化列表(不更改其类型)?

c++ effective-c++ c++11 stdarray

11
推荐指数
1
解决办法
1483
查看次数

为什么需要null shared_ptr以及如何使用它?

在Scott Meyers的Effective C++中,第18项使接口易于正确使用且难以正确使用,他提到了null shared_ptr:

std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0), getRidOfInvestment)
Run Code Online (Sandbox Code Playgroud)

和一个时尚分配操作

pInv = ...     //make retVal point to the correct object
Run Code Online (Sandbox Code Playgroud)

在这种情况下,可能需要创建一个null shared_ptr并稍后进行分配?为什么不在有资源(原始指针)时创建shared_ptr?

由于Scott Meyers没有在前面的例子中显示完整的赋值,我认为shared_ptr的assign运算符是重载的,可以这样做:

pInv = new Investment;    // pInv will take charge of the pointer
                          // but meanwhile keep the delete function it already had
Run Code Online (Sandbox Code Playgroud)

但我尝试使用boost的实现它不会这样工作.那么null shared_ptr是什么意思?

我几乎可以肯定我在这里遗漏了一些东西,有人帮我解决了.

PS.更多关于shared_ptr的初始化和赋值

#include <boost/shared_ptr.hpp>

int main(int argc, char *argv[])
{
    boost::shared_ptr<int> ptr1(new int);
    boost::shared_ptr<int> ptr2;
    ptr2.reset(new int);
    boost::shared_ptr<int> ptr3 = new int;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个例子不能由g ++(Ubuntu/Linaro …

c++ shared-ptr effective-c++ c++11

9
推荐指数
2
解决办法
2万
查看次数

C++:Scott Meyers"Effective STL":第31项:了解您的排序选项:帮助理解

美好的一天!

在他的"有效STL"中,Scott Meyers写道

第三种方法是使用迭代器的有序容器中的信息迭代地将列表元素拼接到您希望它们所在的位置.正如您所看到的,有很多选项.(第31项,第2部分)

有人可以这样解释我吗?


更多文字(了解背景):

算法sort,stable_sort,partial_sort和nth_element需要随机访问迭代器,因此它们只能应用于向量,字符串,deques和数组.在标准关联容器中对元素进行排序是没有意义的,因为这样的容器使用它们的比较函数始终保持排序.我们可能希望使用sort,stable_sort,partial_sort或nth_element但不能的唯一容器是list,并且list通过提供其排序成员函数来进行补偿.(有趣的是,list :: sort执行稳定的排序.)如果要对列表进行排序,则可以,但如果要对列表中的对象使用partial_sort或nth_element,则必须间接执行.一种间接方法是将元素复制到具有随机访问迭代器的容器中,然后将所需的算法应用于该容器.另一种方法是创建一个list :: iterators的容器,在该容器上使用该算法,然后通过迭代器访问列表元素.第三种方法是使用迭代器的有序容器中的信息迭代地将列表元素拼接到您希望它们所在的位置.正如您所看到的,有很多选项.

c++ sorting stl list effective-c++

9
推荐指数
1
解决办法
1904
查看次数

我可以使用流操作符重写日志宏以使用C++模板函数吗?

我们的项目使用宏来使单行语句中的日志记录变得简单和简单,如下所示:

DEBUG_LOG(TRACE_LOG_LEVEL, "The X value = " << x << ", pointer = " << *x);
Run Code Online (Sandbox Code Playgroud)

宏将第二个参数转换为stringstream参数,并将其发送到常规C++记录器.这在实践中非常有用,因为它使多参数记录语句非常简洁.然而,斯科特·迈尔斯说,在有效的C++第3版,(第2项)"您可以通过使用一个内联函数模板获得的宏所有的效率加上所有常规函数的可预测的行为和类型安全".我知道在C++中与可预测行为相关的宏用法有很多问题,所以我试图在代码库中尽可能多地消除宏.

我的日志宏定义类似于:

#define DEBUG_LOG(aLogLevel, aWhat) {  \
if (isEnabled(aLogLevel)) {            \
  std::stringstream outStr;            \
  outStr<< __FILE__ << "(" << __LINE__ << ") [" << getpid() << "] : " << aWhat;    \
  logger::log(aLogLevel, outStr.str());    \
}
Run Code Online (Sandbox Code Playgroud)

我已多次尝试将其重写为不使用宏的内容,包括:

inline void DEBUG_LOG(LogLevel aLogLevel, const std::stringstream& aWhat) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

和...

template<typename WhatT> inline void DEBUG_LOG(LogLevel aLogLevel, WhatT aWhat) {
    ...  }
Run Code Online (Sandbox Code Playgroud)

无济于事(以上两个重写都不会在第一个例子中编译我们的日志代码).还有其他想法吗?可以这样做吗?或者最好把它留作宏?

c++ macros effective-c++

8
推荐指数
2
解决办法
3537
查看次数

用C++模板,为什么要使用枚举

关于Scott Meyers的"Effective C++"中的第48项,我有一个简短的问题.我只是不明白从下面的书中复制的代码,

    #include <iostream>
    using namespace std;

    template <unsigned n>
    struct Factorial
    {
       enum { value=n*Factorial<n-1>::value };
    };

    template <>
    struct Factorial<0>
    {
        enum { value=1};
    };

    int main()
    {
        cout<<Factorial<5>::value<<endl;
        cout<<Factorial<10>::value<<endl;
    }
Run Code Online (Sandbox Code Playgroud)

为什么我必须在模板编程中使用枚举?有没有其他方法可以做到这一点?我在这里先向您的帮助表示感谢.

c++ effective-c++

8
推荐指数
2
解决办法
1249
查看次数

使用 c++ 20 中的概念,通用引用的重载现在更加安全

Scott Meyers 所著的《Effective Modern C++》一书中给出了“避免通用引用重载”的建议(第 26/27 项)。他的理由是,在几乎所有对包含通用引用的重载函数的调用中,编译器都会解析为通用引用,即使这通常不是您想要解析的函数。(所以我认为这段代码很糟糕?)

template <typename T>
void foo(T&& t) {
  // some sort of perfect forwarding
}
void foo(string&& t) {
  // some sort of move operation
}
Run Code Online (Sandbox Code Playgroud)

上面的例子是精心设计的,可能会被替换为 2 个函数。

我认为更难解决且不那么做作的另一个例子是他在第 26 项中实际给出的例子。

class Foo {
// a decent amount of private data that would take a while to copy
public:
  // perfect forwarding constructor, usually the compiler resolves to this...
  template <typename T>
  explicit Foo(T&& t) : /* forward the information for construction */ …
Run Code Online (Sandbox Code Playgroud)

c++ effective-c++ c++-concepts forwarding-reference c++20

7
推荐指数
1
解决办法
611
查看次数

符号表由C++编译器创建

我读的有效C++,第3版和第2项(喜欢常量,枚举和内联到#定义),斯科特迈尔斯提到的符号表:他解释说,#defines可能不会出现在符号表.

基于答案在这里,有点的建议阅读它们,和维基百科的文章,我会按照如下方式定义符号表:因为编译器只为每一个翻译单元创建对象文件,我们还需要一种方式来引用符号之间翻译单位.这是使用为每个目标文件创建的表来完成的,以便可以在以后阶段定义符号 - 当从目标文件创建可执行文件/库时,链接器可以定义符号.在链接期间,符号由链接器替换为其适当的内存地址.

这是我想知道的:

  • 我的解释是否正确?
  • 链接后,一旦解析了内存地址,我认为不需要符号表吗?也就是说,我认为符号表在可执行文件/库中不可用; 那是对的吗?
  • 我怀疑符号表对其他编译器任务也有用吗?是否可能会识别命名冲突?
  • 上述符号表与导出表不同.至少在Visual C++的上下文中,导出表定义了在库外明确声明为可见的符号.我想在某种意义上说这是一个符号表 - 但与Scott所指的符号表无关.
  • 符号表还有什么有趣的吗?也就是说,我应该对符号表有任何额外的见解吗?

感谢您的时间和贡献.

c++ compiler-construction symbols effective-c++ visual-c++

6
推荐指数
1
解决办法
1344
查看次数

在没有返回值优化的情况下将两个对象添加到一起时会创建多少个临时对象?

在阅读Scott Meyers所着的"更有效的C++"一书的第20和22项后,我决定提出这个问题.

假设你写了一个代表有理数的类:

class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);

    int numerator() const;
    int denominator() const;

    Rational& operator+=(const Rational& rhs); // Does not create any temporary objects
    ...
};
Run Code Online (Sandbox Code Playgroud)

现在让我们说你决定operator+使用operator+=:

const Rational operator+(const Rational& lhs, const Rational& rhs)
{
    return Rational(lhs) += rhs;
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:如果禁用返回值优化,将创建多少个临时变量operator+

Rational result, a, b;
...
result = a + b;
Run Code Online (Sandbox Code Playgroud)

我相信会创建2个临时值:一个Rational(lhs)是在body体内执行的operator+,另一个operator+是在返回的值是通过复制第一个临时值创建的.

当Scott提出这个操作时,我的困惑出现了:

Rational result, a, b, …
Run Code Online (Sandbox Code Playgroud)

c++ effective-c++ operator-keyword return-value-optimization copy-elision

6
推荐指数
1
解决办法
153
查看次数