继续问复制控制

Dan1980 2007-01-05 04:10:21
一个非常简单的程序如下:

#include <iostream>

using namespace std;

class Foo {
public:
Foo(int i) { cout << "using int. done." << endl; }
~Foo() { cout << "deleting. done." << endl; }
//private:
Foo(const Foo &f) { cout << "using copying. done." << endl; }
};

int main() {
Foo f = 100;
return 0;
}


运行结果为:
using int. done.
deleting. done.

分析Foo f = 100这句,应该是先调用Foo(int)来构造临时对象,再调用拷贝构造函数Foo(const Foo &f)来构造f,然后再删除临时对象。

但奇怪的是:拷贝构造函数Foo(const Foo &f)似乎并没有被调用,析构函数也只被调用了一次;而如果将Foo(const Foo &f)设为private,则不能编译。

谁能解释一下?谢谢
...全文
449 21 打赏 收藏 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
Dan1980 2007-02-14
  • 打赏
  • 举报
回复
自己找到答案了:

《C++ Primer》上说:

The copy form of initialization is primarily supported for compatibility with C usage. When it can do so, the compiler is permitted (but not obligated) to skip the copy constructor and create the object directly.
复制形式的初始化(即用=号初始化,本人注)主要是为了兼容C的用法的。当这种用法被支持时,编译器被允许(但不被制强)跳过复制构造函数而直接创建该对象。

我想这段话已经回答我的问题了。

感谢楼上诸位,特别是abblly(东边日出西边雨)。

结贴。
abblly 2007-01-05
  • 打赏
  • 举报
回复
楼上的是翻译还是自己写的,乱七八糟的.
-----------
不是我自己写的,我还没有这么高的水平~-~
转载别人的,哪里乱了,那篇文章把对象的构造
T a;
T a(v);
T a = v;
的各种情况都说到了,而且是参考的C++标准,感觉说的既全又对阿
htqx 2007-01-05
  • 打赏
  • 举报
回复

我认为这个规定毫无必要.

T v = 0;
T v(0);

唯一的差别就是explicit 关键字.
htqx 2007-01-05
  • 打赏
  • 举报
回复
楼上的是翻译还是自己写的,乱七八糟的.
abblly 2007-01-05
  • 打赏
  • 举报
回复
不过vc6也有问题,Foo f = 100; 这句它可能直接解释为Foo f(100);了,但根据《C++ Primer》中的介绍,应该不是这样的。
----------------
不是应该不是这样的,详见我转载文章的第三种情况
----
3。T a = v;
....
之所以强调语义上,是因为标准说了这个调用可以优化掉...
--------------
Dan1980 2007-01-05
  • 打赏
  • 举报
回复
to abblly(西边日出东边雨):

哦,那有可能是g++的问题了。

不过vc6也有问题,Foo f = 100; 这句它可能直接解释为Foo f(100);了,但根据《C++ Primer》中的介绍,应该不是这样的。
abblly 2007-01-05
  • 打赏
  • 举报
回复
Foo f = 100;
等於
Foo f(100);
是这样吗?
----------
no,
Foo f = 100;相当于我转载文章的第三种情况
Foo f(100);//我转载文章的第二种情况


linjuan0215 2007-01-05
  • 打赏
  • 举报
回复
Foo f = 100;
等於
Foo f(100);
是这样吗?


lw1a2 2007-01-05
  • 打赏
  • 举报
回复
有个帖子,回家找找看看
abblly 2007-01-05
  • 打赏
  • 举报
回复
to Dan1980() ( ) 信誉:98 Blog
这个是实现相关的,我用vc6可以编译通过
你可以看看我转载的回答
taodm 2007-01-05
  • 打赏
  • 举报
回复
抱歉,我只有纸版。
Dan1980 2007-01-05
  • 打赏
  • 举报
回复
to taodm(taodm):
手上没有execptional C++,能否将相关内容转述一下?
Dan1980 2007-01-05
  • 打赏
  • 举报
回复
to abblly(西边日出东边雨)
--------------------------------
are u sure???
--------------------------------

Yes, i am sure. you may try it yourself.




to zhousqy(标准C匪徒)(甩拉,甩拉)
------------------------------------
Foo f = 100;
等於
Foo f(100);
------------------------------------
既然是这样,那为什么将Foo(const Foo &f)设为private就不能编译呢?


abblly 2007-01-05
  • 打赏
  • 举报
回复
转载:

发信人: NetMD (见到了传说中的神!!), 信区: CPlusPlus
标 题: [FAQ]T a(v);和T a=v;的区别(3)
发信站: 水木社区 (Fri Mar 31 12:18:10 2006), 站内

本文所说的类型T均指UDT,非built-in类型
感谢ilovecpp,所有“*注”部分由他提醒而补充

构造一个对象,有如下三种形式:

1。T a;
这个没什么好说的,调用default ctor来构造a
不过要注意的是,要么T就一个ctor也没有,编译器合成default ctor,即T::T()
如果T有手动添加的其他形式的ctor,但是没有T::T(),则此语句报错,因为编译器不再
为T合成default ctor
*注1,如果没有default ctor,但是有某个ctor的所有参数都有缺省值,则T a;也成立


2。T a(v);
这个语句显式用v作为参数调用T的某个最适合的可单参数调用的ctor来构造a
该语句形式被C++标准称为direct-initialization
这里强调两点,一个是显式,这说明这种方式构造a的话可以调用被explicit声明的ctor
第二点是最适合,这是由overload rules决定的,而不是那么想当然,示例代码如下
struct T
{
T(){}
T(int){}
operator int(){return 0;}
private:
T(T&){}
};
T foo() {return T();}
int main()
{
T a;

T b(a); // 编译出错,最适合的T(T&)不可访问,为private

T c(foo());// 编译成功,foo()返回的临时对象是rvalue,不能绑定到non-const引用
// 因此T(T&)不是由overload rules选定的最适合ctor
// 但是rvalue可以调用一次自定义隐式转换cast到int然后调用T(int)构造c
// 也就是说,overload rules选择了T(int)

return 0;
}
这段代码说明,即使类型相同,调用的也不一定是copy ctor
如果有人怀疑T(T&)不是copy ctor,认为T(const T&)才是,请参阅C++标准12.8


3。T a = v;
该语句形式被C++标准称为copy-initialization,这个名称有很大的迷惑性,导致本人曾
一度认为该式语义上(稍后解释什么是语义上)必须要调用copy ctor
参照C++标准8.5/14,有这么一句话
If the destination type is a (possibly cv-qualified) class type:
--If the initialization is direct-initialization, or if it is
copy-initialization where the cv-unqualified version of the
source type is the same class as, or a derived class of, the
class of the destination, constructors are considered. The
applicable constructors are enumerated (13.3.1.3), and the
best one is chosen through overload resolution (13.3). The
constructor so selected is called to initialize the object,
with the initializer expression(s) as its argument(s). If no
constructor applies, or the overload resolution is ambiguous,
the initialization is ill-formed.
简单点说,上面的意思就是,如果T是UDT,在“copy-initialization且v的cv-
unqualified类型也为T(或者T的派生类)”的情况下,执行方式同
direct-initialization一样!
*注2,实际上该情况中的copy-initialization同direct-initialization还是有区别的,
* C++标准指出,只有T a(v);的方式才是显式调用ctor,否则为隐式调用,后者要求
* 被选中的ctor不能被声明为explicit,否则编译出错
*---C++标准12.3.1
* An explicit constructor constructs objects just like non-explicit
* constructors, but does so only where the direct-initialization
* syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used.
C++标准还规定,如果非上述情况,则有
--Otherwise (i.e., for the remaining copy-initialization cases),
user-defined conversion sequences that can convert from the source
type to the destination type or (when a conversion function is used)
to a derived class thereof are enumerated as described in 13.3.1.4,
and the best one is chosen through overload resolution (13.3). If the
conversion cannot be done or is ambiguous, the initialization is ill-formed.
The function selected is called with the initializer expression as its
argument; if the function is a constructor, the call initializes a
temporary of the destination type. The result of the call (which is
the temporary for the constructor case) is then used to direct-initialize,
according to the rules above, the object that is the destination of
the copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
简单点说,就是语句T a = v;在当v的类型不同于T(也不是T的派生类)的时候,语义上
要先用v构造一个临时对象T(或者调用自定义转换隐式cast到T类型或者T的派生类型),
然后显式调用copy ctor来构造a
之所以强调语义上,是因为标准说了这个调用可以优化掉,但是语义存在
而强调显式,则说明copy ctor可以为explicit
此外需要强调的就是虽然标准在这里说构造了临时对象T(v)之后(或者是将v隐式转换
为类型T,static_cast<T>(v))再用direct-initialize直接构造a,整个语句总的执行
形式为T a(T(v));(static_cast<T>(v)用C语法也可记为T(v)),但是用T(v)所调用的
ctor只能是T的copy ctor,原因是编译器已经进行了一次自定义的隐式转换T(v),不能
再进行第二次,所以必须调用对T(v)直接参数匹配的ctor,这就是copy ctor
对上面的句子还有一个补充,就是派生类向基类的cast不算自定义隐式转换,是自动发
生的,所以还有一种选择就是T a(U(v));而U是T的派生类!最终调用的仍旧是copy ctor
示例如下
struct T
{
T(){}
T(int){}
operator int(){return 0;}
private:
explicit T(T&){}
};
int main()
{
T a = T(); // 编译通过,相当于隐式调用T a(T());
// 而重载规则选用了T(int),即T a(static_cast<int>(T()));
// 如果T::T(int)声明为explicit,编译出错

T b = 0; // 编译出错,copy ctor不能访问,虽然被优化这个调用,但是语义尚存

return 0;
}

下面再用一个示例补充说明
struct T
{
T(int){}
explicit T(const T&){}
};
void foo(T)
{
}
int main()
{
foo(T(0)); // 编译出错,按照*注2,要求隐式拷贝给foo的参数
// 相当于T t = T(0);

foo(0); // 编译通过,用0构造一个临时对象T(0),然后显式拷贝给foo的参数
// 相当于T t = 0;

return 0;
}
由于函数的按值传参和按值返回都是copy-initialization,所以会有如上的结果差异
taodm 2007-01-05
  • 打赏
  • 举报
回复
execptional C++ item42
huangyangman 2007-01-05
  • 打赏
  • 举报
回复
同意zhousqy(标准C匪徒)(甩拉,甩拉)
zhousqy 2007-01-05
  • 打赏
  • 举报
回复
Foo f = 100;
等於
Foo f(100);
todototry 2007-01-05
  • 打赏
  • 举报
回复
分析Foo f = 100这句,应该是先调用Foo(int)来构造临时对象,再调用拷贝构造函数Foo(const Foo &f)来构造f,然后再删除临时对象。
============================================
理论上,是这样的执行
zhousqy 2007-01-05
  • 打赏
  • 举报
回复
Foo f = 100;
等於
Foo f(100);
abblly 2007-01-05
  • 打赏
  • 举报
回复
------
将Foo(const Foo &f)设为private,则不能编译。
---------
are u sure???
加载更多回复(1)
内容概要:本文围绕基于物理场的动态模式分解(piDMD)展开研究,重点探讨其在流体力学、热传导及多物理耦合系统中的建模与应用。通过Matlab代码实现,将传统的动态模式分解(DMD)方法与物理场控制方程相结合,引入物理约束以增强数据驱动模型的物理一致性,从而提升对复杂非线性系统的模态提取精度与长期预测能力。文章系统阐述了piDMD的理论基础、数学推导过程与算法实现步骤,并结合典型物理场仿真案例(如Navier-Stokes方程驱动的流场演化)进行验证,展示了其在降阶建模、主导模态识别和时空动态可视化方面的优越性能,适用于高维、强耦合、非平稳系统的分析与优化。; 适合人群:具备一定数值计算、流体力学、控制理论或数据驱动建模背景,熟悉Matlab编程与线性代数运算,从事科学研究、工程仿真或系统辨识工作的研究生、科研人员及工程师。; 使用场景及目标:①实现对流场、温度场等物理场的高效降阶建模与动态行为预测;②提取符合物理规律的关键模态,用于系统稳定性分析与控制设计;③支撑复杂系统的状态估计、异常检测与优化调控等下游任务。; 阅读建议:建议读者结合提供的Matlab代码逐行调试与运行,深入理解piDMD算法中物理约束的嵌入方式及其对模态质量的影响,同时可尝试将其迁移至其他偏微分方程描述的物理系统中进行拓展验证与应用。
内容概要:本文研究了在峰谷分时电价引导下,基于多目标优化遗传算法NSGA-II的电动汽车充电负荷优化问题。通过构建以电网负荷波动最小化和用户充电成本最低为核心的多目标优化模型,利用NSGA-II算法求解获得帕累托最优解集,实现对大规模电动汽车充电行为的有序引导。文中系统阐述了峰谷电价激励机制的设计原理、电动汽车充电负荷的数学建模方法、多目标优化问题的形式化表达以及NSGA-II算法的具体实现流程,并借助Matlab平台进行仿真验证,结果表明该策略能有效平抑电网负荷峰谷差、降低用户充电支出并提升电力系统的运行效率与稳定性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事智能电网、电动汽车调度、需求侧管理等相关领域的研究生或科研人员。; 使用场景及目标:①研究峰谷电价政策对电动汽车充电行为的影响机制;②掌握NSGA-II算法在电力系统多目标优化中的具体应用;③实现电动汽车集群的有序充电调度以改善电网负荷曲线; 阅读建议:此资源结合了电力经济学与智能优化算法,建议读者在学习过程中重点关注模型构建与算法实现的衔接部分,动手复现仿真代码以深入理解参数设置与结果分析过程。
详情可查看下方数据集可视化效果。 【数据集概况】 · 检测类别(中文):[电梯(elevator)] · 训练集:1468 张 · 验证集:157 张 · 测试集:158 张 · 总计:1783 张 该数据集聚焦于各类建筑内部及外部环境中的电梯目标检测任务,涵盖住宅楼、办公楼、商场、酒店等多种典型场景下的电梯结构。图像采集覆盖不同光照条件、材质类型与安装位置,充分体现了实际应用中电梯的多样性与复杂性,为智能楼宇管理、安防监控及无障碍设施识别等方向提供了高质量的视觉基础支持。... 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 72 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.9446** mAP50-95 | 0.6531 Precision | 0.9305 Recall | 0.8777 train/box_loss | 0.5807 train/cls_loss | 0.3505 val/box_loss | 1.0020 val/cls_loss | 0.6208 【训练过程分析】 72 轮训练后 mAP50 达到 0.9446,模型收敛良好。Loss 曲线前段快速下降,后段趋于平稳,val_loss 无反弹,没有明显过拟合。但 mAP50-95 为 0.6531,和 mAP50 差距 0.29,定位精度仍有优化空间。 【模型性能评估】 Precision 0.9305、Recall 0.8777,精度高于召回,存在一定漏检。 【预测效果展示】 验证集预测效果较好,检测框基本准确覆盖电梯,置信度整体偏高。 【改进建议】 1. 丰富场景...

65,213

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧