拷贝构造函数以值传递的方式给函数参数传值

本文深入解析C++中拷贝构造函数如何实现值传递,通过实例代码演示拷贝构造函数在函数参数传值过程中的作用。文章详细解释了拷贝构造函数的调用时机,参数传递方式以及其内部实现机制。

拷贝构造函数给函数参数传值

我对 C++中 拷贝构造函数 以值传递的方式 给函数参数传值 的理解

还有,为什么是值传递呢?

要看懂本文章你需要了解:

  1. C++基础语法
  2. C++中的引用
  3. C++中的class(类和对象)
  4. 构造函数和析构函数
  5. 构造函数的分类及调用

注意:请按照编号顺序查看代码说明,请从下往上看


class Person
{

public:

    
	Person()
	{
		cout << "默认构造函数调用" << endl;
	}
	
	Person(int age)
	{
		m_Age = age;
		
		cout << "有参构造函数调用" << endl;
	}


—————————————————————————————————————————————————————————————————————————————————————————————
	
	
	// 8.因为doWork传入的参数是p1,所以会自动调用拷贝构造函数

	// p1将会以引用的方式传入拷贝构造函数。

	// 所以,当编译器发现是引用,会自动转化 const Person& p 为 const int* const p = &p1

	// 注意,这里的p1是只读的,因为被const修饰了,不可修改,p1的作用域也只限于拷贝构造函数内。

	Person(const Person& p)
	{

		// 9.注意,拷贝构造函数是以引用作为它的参数,其实在这个函数内还是可以修改实参的

		//p.m_Age = 12; 因为拷贝构造函数使用了引用,所以去掉const就可以更改实参了


		// 10.我们知道构造函数的一个目的是进行初始化操作

		// 我们将m_Age初始化,在这里,我们就算实现了所谓的值传递

		m_Age = p.m_Age;

		cout << "拷贝构造函数调用" << endl;
	}


	~Person001()
	{
		cout << "析构函数调用" << endl;
	}
	
	int m_Age;
};


// 7.这里相当于写了 Person p = p1;

// 使用了隐式转换法来调用 (调用的方法有3种,括号法、显示法、隐式转换法,请自行去了解)

// 创建对象的时候,构造函数会自动调用,而且只调用一次。

// 因为传入的参数是p1所以,会自动调用拷贝构造构造函数

void doWork(Person p) //这里会自动调用拷贝构造函数
{
    // 11.好,回到这里

    // p.m_Age原本等于10,我们现在尝试修改p.m_Age为12,看看能不能改变实参

	p.m_Age = 12;
}


void test()
{
    // 3.实例化对象p1,运行这行代码时会调用“默认构造函数”,然后在text()函数销毁前执行“析构函数”

	Person p1;
    

    // 4.给p1中的m_Age赋值为10

	p1.m_Age = 10;


    // 5.屏幕显示:Person age = 10

	cout << "Person age = " << p1.m_Age << endl; 
    

    // 6.p1做为参数传入doWork函数,注意这里是值传递(那为什么呢?)

	doWork(p1);


    // 12.并不能改变实参,所以我们可以知道,拷贝构造函数 以值传递的方式 给函数参数 传值

	cout << "Person age = " << p1.m_Age << endl;
}


// 1.首先看这里,这里是主函数,程序的开始

int main()
{
    // 2.调用test函数

	test();

	system("pause");

	return 0;
}

如果文章有错误,欢迎指正:2892870137@qq.com

欢迎拜访我的主博客:https://h-james.github.io/

C++中,**参(即按值传递参数)时一定会调用拷贝构造函数**(或移动构造函数,若参数为右且存在移动语义)。这是C++对象传递的核心机制之一,其目的是在函数内部创建参数的独立副本。以下是详细分析: --- ### **1. 参的基本行为** #### **(1)拷贝构造函数的调用时机** 当函数参数值传递时,编译器会**在函数调用处构造参数的副本**,此时会调用拷贝构造函数(或移动构造函数)。 **示例**: ```cpp #include <iostream> class MyClass { public: MyClass() { std::cout << "Default constructor\n"; } MyClass(const MyClass& other) { std::cout << "Copy constructor\n"; } }; void foo(MyClass obj) { // 按值传递 std::cout << "Inside foo\n"; } int main() { MyClass a; // 调用默认构造函数 foo(a); // 调用拷贝构造函数构造obj的副本 return 0; } ``` **输出**: ``` Default constructor Copy constructor // 时调用拷贝构造函数 Inside foo ``` --- ### **2. 拷贝构造函数与移动构造函数的调用规则** #### **(1)左参数:调用拷贝构造函数** 若传递的参数是左(如变量、表达式结果为左),编译器会调用拷贝构造函数: ```cpp void bar(MyClass obj) {} int main() { MyClass a; bar(a); // 左a,调用拷贝构造函数 return 0; } ``` #### **(2)右参数:优先调用移动构造函数(若存在)** 若传递的参数是右(如临时对象、字面量、`std::move`转换的结果),且类定义了移动构造函数,编译器会优先调用移动构造函数以提高效率: ```cpp class MyClass { public: MyClass() { std::cout << "Default\n"; } MyClass(const MyClass&) { std::cout << "Copy\n"; } MyClass(MyClass&&) { std::cout << "Move\n"; } // 移动构造函数 }; void baz(MyClass obj) {} int main() { baz(MyClass()); // 临时对象(右),调用移动构造函数 MyClass a; baz(std::move(a)); // 显式转换为右,调用移动构造函数 return 0; } ``` **输出**: ``` Default Move // 临时对象调用移动构造函数 Default Move // std::move(a)调用移动构造函数 ``` --- ### **3. 特殊场景:隐式类型转换与拷贝构造** 若参数类型与实参类型不完全匹配,但存在隐式类型转换,编译器会先构造临时对象,再调用拷贝构造函数: ```cpp class Wrapper { public: Wrapper(int x) { std::cout << "Converting constructor\n"; } Wrapper(const Wrapper&) { std::cout << "Copy constructor\n"; } }; void func(Wrapper w) {} int main() { func(42); // 1. 调用Wrapper(int)构造临时对象 2. 调用拷贝构造函数传递副本 return 0; } ``` **输出**: ``` Converting constructor Copy constructor ``` --- ### **4. 性能优化:避免不必要的拷贝** #### **(1)使用`const`引用传递** 若无需修改参数且希望避免拷贝,可按`const`引用传递: ```cpp void efficientFoo(const MyClass& obj) { // 无拷贝 std::cout << "Inside efficientFoo\n"; } int main() { MyClass a; efficientFoo(a); // 无拷贝构造函数调用 return 0; } ``` #### **(2)使用右引用与移动语义** 若需修改参数或支持移动语义,可按右引用传递: ```cpp void moveAwareFoo(MyClass&& obj) { // 仅接受右 std::cout << "Inside moveAwareFoo\n"; } int main() { moveAwareFoo(MyClass()); // 移动构造临时对象 MyClass a; moveAwareFoo(std::move(a)); // 显式移动 return 0; } ``` --- ### **5. 拷贝构造函数的显式禁用** 若类中显式删除了拷贝构造函数(`= delete`),按值传递会导致编译错误: ```cpp class NonCopyable { public: NonCopyable() = default; NonCopyable(const NonCopyable&) = delete; // 禁止拷贝 }; void errorFunc(NonCopyable obj) {} int main() { NonCopyable a; errorFunc(a); // 编译错误:调用已删除的拷贝构造函数 return 0; } ``` --- ### **6. 总结** | **方式** | **构造函数调用** | **适用场景** | |--------------------|--------------------------------------|----------------------------------| | 按值传递(左) | 调用拷贝构造函数 | 需要参数副本,不关心性能开销 | | 按值传递(右) | 优先调用移动构造函数(若存在) | 临时对象或显式移动的右 | | `const`引用传递 | 无拷贝 | 避免拷贝,仅需只读访问 | | 右引用传递 | 无拷贝(直接操作原对象) | 需要修改临时对象或支持移动语义 | **核心原则**: - **参必然涉及拷贝或移动构造**,这是C++对象语义的直接体现。 - **优化策略**:根据需求选择引用传递(`const`或右)以避免不必要的拷贝。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值