C++ 初始化 pointer/reference/copy 细微差别
C++ initializing pointer/reference/copy nuances
上下文:嵌入式编程和初始化列表的细微差别,尤其是我认为应该调用复制构造函数的那个。
问题: 一个class在另一个class的初始化列表中的实例化是否会调用复制构造函数来初始化一个持有值的成员变量?
下面提供的示例中,TestClassCopy 有一个成员变量 TestMember,按值保存,而不是指针或引用。 cppreference page 似乎没有在提供的示例中充分涵盖这一点。
奖金问题:在初始化列表中调用的复制构造函数是否会产生任何 time/space 性能影响?如果 C++ 规范允许,似乎编译器应该能够优化它。
这是代码(使用 VS2015 工具链测试构建):
TestMember.h(由于 space 原因未显示 TestMember.cpp)
#pragma once
#include <stdint.h>
class TestMember {
public:
TestMember(uint8_t);
private:
uint8_t m_value;
};
TestClassCopy.h
#pragma once
#include "test_member.h"
class TestClassCopy {
public:
TestClassCopy();
virtual ~TestClassCopy();
private:
TestMember m_member;
};
TestClassCopy.cpp
#include "test_class_copy.h"
TestClassCopy::TestClassCopy() :
m_member(TestMember(255)) { // invokes copy constructor yes?
}
TestClassCopy::~TestClassCopy() {
}
为了完整起见,我可能会做出我不应该做出的假设的其他事情:
对于指向 TestMember 的成员指针,初始化列表中的 'new' 和析构函数中的 'delete' 就足够了。
对于成员引用,我的理解是,如果将引用传递到构造函数中,则存在更多细微差别,您可以直接分配它(因为生命周期是在初始化列表之外管理的)。 但是,如果 TestMember 在初始化列表中实例化(进入引用),那么这是不行的,因为 TestMember 临时在初始化完成后消失。
TestMemberReference.h
#pragma once
class TestMember;
class TestClassReference {
public:
TestClassReference();
virtual ~TestClassReference();
private:
TestMember& m_member;
};
TestMemberReference.cpp
#include "test_class_reference.h"
#include "test_member.h"
TestClassReference::TestClassReference() :
m_member(TestMember(255)) { // ew, don't do this; TestMember temporary will go out of scope
}
TestClassReference::~TestClassReference() {
}
This cppreference page does not seem to cover this sufficiently in the examples provided
那个 cppreference 页面说 "Initializes the base or member named by class-or-identifier using direct initialization" 如果你点击它,你可以阅读它,从 C++17 开始,
if the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision
所以答案是:在 C++17 中没有调用复制构造函数。在 C++17 之前,技术上可以调用复制构造函数,但复制省略消除了该调用。您仍然可以通过使用 g++ -fno-elide-constructors -O0 -std=c++14
编译您的示例来观察它,作为证据
上下文:嵌入式编程和初始化列表的细微差别,尤其是我认为应该调用复制构造函数的那个。
问题: 一个class在另一个class的初始化列表中的实例化是否会调用复制构造函数来初始化一个持有值的成员变量? 下面提供的示例中,TestClassCopy 有一个成员变量 TestMember,按值保存,而不是指针或引用。 cppreference page 似乎没有在提供的示例中充分涵盖这一点。
奖金问题:在初始化列表中调用的复制构造函数是否会产生任何 time/space 性能影响?如果 C++ 规范允许,似乎编译器应该能够优化它。
这是代码(使用 VS2015 工具链测试构建):
TestMember.h(由于 space 原因未显示 TestMember.cpp)
#pragma once
#include <stdint.h>
class TestMember {
public:
TestMember(uint8_t);
private:
uint8_t m_value;
};
TestClassCopy.h
#pragma once
#include "test_member.h"
class TestClassCopy {
public:
TestClassCopy();
virtual ~TestClassCopy();
private:
TestMember m_member;
};
TestClassCopy.cpp
#include "test_class_copy.h"
TestClassCopy::TestClassCopy() :
m_member(TestMember(255)) { // invokes copy constructor yes?
}
TestClassCopy::~TestClassCopy() {
}
为了完整起见,我可能会做出我不应该做出的假设的其他事情:
对于指向 TestMember 的成员指针,初始化列表中的 'new' 和析构函数中的 'delete' 就足够了。
对于成员引用,我的理解是,如果将引用传递到构造函数中,则存在更多细微差别,您可以直接分配它(因为生命周期是在初始化列表之外管理的)。 但是,如果 TestMember 在初始化列表中实例化(进入引用),那么这是不行的,因为 TestMember 临时在初始化完成后消失。
TestMemberReference.h
#pragma once
class TestMember;
class TestClassReference {
public:
TestClassReference();
virtual ~TestClassReference();
private:
TestMember& m_member;
};
TestMemberReference.cpp
#include "test_class_reference.h"
#include "test_member.h"
TestClassReference::TestClassReference() :
m_member(TestMember(255)) { // ew, don't do this; TestMember temporary will go out of scope
}
TestClassReference::~TestClassReference() {
}
This cppreference page does not seem to cover this sufficiently in the examples provided
那个 cppreference 页面说 "Initializes the base or member named by class-or-identifier using direct initialization" 如果你点击它,你可以阅读它,从 C++17 开始,
if the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision
所以答案是:在 C++17 中没有调用复制构造函数。在 C++17 之前,技术上可以调用复制构造函数,但复制省略消除了该调用。您仍然可以通过使用 g++ -fno-elide-constructors -O0 -std=c++14