首页 > 其他分享 >cpp类对象作函数参数

cpp类对象作函数参数

时间:2022-08-30 22:12:32浏览次数:79  
标签:函数 对象 t2 t3 函数参数 test str cpp 拷贝

cpp类对象作函数参数

参考:c++ Prime Plus(第六版) 第12章

传址or传值

函数传参有几种:指针、引用、值;前两个都是传入地址,最后一个传入对象的值

由于指针操作数据的灵活性高,且指针指的是一个地址,极容易直接修改这个地址里存储的数据,所以出现了引用(指针的语法糖)

引用的好处:

  1. 可以修改调用调用函数中的数据对象(在使用指针时,我们能加const就会加上,以免误操作)
  2. 传递的是引用,不是整个数据对象(提高层序运行速度)

为什么不能用传值代替传入const指针或者引用?

传值对于函数,它只能操作这个值,不能修改原数据;由此联想,我们传递一个值时可能会让程序更快的运行。但实际上传值并不安全,或者说容易在意想不到的地方挖坑,同时,在传递一个比较大类对象时,它的速度并不快

这里创建了一个test类,让我们在类外函数调用类对象看看会出现什么问题:

#include<string.h>
#include<iostream>

class test {
private:
	char* str;
	int len;
public:
	test(const char* s);
	~test();
	friend std::ostream& operator <<(std::ostream& os, const test& one);
};
test::test(const char* s) {
	len = strlen(s);
	str = new char[len + 1]();// 这里需要有()!否则会有想不到的奇怪问题
	strcpy_s(str,len+1, s);
}
test::~test() {
	std::cout << "goodbye," << str << std::endl;
    // 避免重复调用构造函数
	if (str) {
		delete[] str;
		str = nullptr;
	}
}
std::ostream& operator<<(std::ostream& os, const test& one) {
	os << one.str;
	return os;
}
void callme(test& rsb) {
	std::cout << "wanna test 传址:" << rsb << "\n";
}
void callme2(test sb) {
	std::cout << "wanna test 传值:" << sb << "\n";
}

int main() {
	test t1("I'm t1");
	test t2("俺是t2");
	// test t3 = t2;
	callme(t1);
	callme2(t2);
	std::cout << "end" << std::endl;
	//	system("pause");
	return 0;
}

让我们执行一下,如果你也用的是VS,那么你应该也会跳出这个页面:image-20220830134544948

最主要的原因应该是内存管理出现了问题,野指针、溢出等等,而不是编译器出了问题

虽然编译器在这里下了断点停止,但我们依旧能查看输出:

image-20220830211613819

可以看到在我们传值后,程序输出了一堆乱码(文本取决于内存的残留)

但似乎传值并没有什么问题,它可以做到正常输出。但真的可以吗?事实上这个输出并不稳定,会随着编译器的改变收到影响;编译器运行时,无法解析传给callme2的类对象,函数也不能识别其原始字符串

这里可以参考c++ Prime Plus第351页内容

深拷贝和浅拷贝

在向callme2函数中传值时,我们原意是传入t2的str值,但是直接传值在cpp里是浅拷贝

  • 浅拷贝:单纯copy了指针的信息;副本指向了原数据的地址
  • 深拷贝:copy数据的同时还拷贝了数据的结构;创建了一块新的地址存储了副本数据

同时,在如果我们去掉析构函数中这一段内容,我们还会发现对于t2,析构函数调用了两次

if(str){
    delete[] str;
    str =nullptr;
}

在callme2函数浅拷贝t2后,t2的生命周期将等同于callme2的函数周期(浅拷贝时副本指向了原有的t2地址,callme函数中的t2又仅为一个局部变量),函数结束时将调用一次析构函数,而main函数结尾时又将调用一次t2的析构函数,此时会出现乱码。这也就是指针悬挂问题。

类对象作参

接着我们上面的代码,添加一个t3对象,调用callme函数看看

// 我们刚才用到的函数
class test{
private:
	char* str;
	int len;
public:
	test(const char* s);
	~test();
	friend std::ostream& operator <<(std::ostream& os, const test& one);
}
void callme(test& rsb);

// 创建的对象
test t1("I'm t1");
test t2("I'm t2");
test t3 = t2; // t3由t2得到
callme(t3);

这里我们将使用t2来初始化t3,同样出现了乱码,说明我们这里仍为浅拷贝,t2和t3分别调用了同一地址的析构函数

image-20220830214251850

复制构造函数

在使用上述语句时,我们都是在用隐式复制构造函数(它是按值进行复制的)

这些问题,都可以通过定义显示复制构造函数解决:它可以copy一个类对象的副本到新的对象中。

test::test(const test& t){
    len = strlen(t.str)+1;
    str = new char [len]();
    strcpy_s(str, len, t.str);
}

可以用以下几种方式创建t3:

test t3(t2);
test t3 = t2;
test t3 = test(t2);
test *t3 = new test(t2);

END

注:笔者才疏学浅,如有错误,请指教!

标签:函数,对象,t2,t3,函数参数,test,str,cpp,拷贝
From: https://www.cnblogs.com/bisa/p/16641056.html

相关文章

  • Python面向对象模板
    内容概要面向对象面向对象前戏对象与类的创建对象独有的数据对象独有的功能动静态方法面向对象三大特性之继承面向对象三大特性之封装property伪装属性面向对象三大......
  • 对象数组HolidayTravel
    根据题目要求编写模拟的程序(1)五一小假期,许多人选择外出旅游。每位乘客(Passenger)最多可以携带3件行李(Luggage)。并且乘客可以选择个人自由行;或者参加旅行社(TravelAgency......
  • 父类指针指向子类对象,子类指针不能指向父类对象
    父类指针指向子类对象,子类指针不能指向父类对象  总结:<1>.当基类指针指向派生类的时候,只能操作派生类从基类中继承过来的数据.(重写虚函数得到的函数也算继承过来的......
  • cpp
    重载>>输出vector以及__int128template<classT,typename=decltype(std::declval<T>().begin()),typename=decltype(std::declval<T>().end()),......
  • Python自学教程12-类和对象怎么用
    Python是一门现代化的编程语言,也是一门面向对象的编程语言。现代编程语言几乎都支持面向对象编程,面向对象编程是最有效的软件编写方法之一。你可以用类和对象来表示现实当......
  • Linq:Distinct()不能排除重复对象的解决方案
    1.数据准备:假设有几个重复数据,如下,(正常使用Distinct()方法,我们想要排除掉重复的对象)usingSystem.Collections.Generic;namespaceLINQTutorial{publicclassS......
  • Python中函数或者类对象带()与不带()的区别——闭包和函数返回时的常见现象
    Python中函数或者类对象带()与不带()的区别-----闭包和函数返回时的常见现象-函数不带括号时,调用的是这个函数本身,是整个函数体,是一个函数对象,不需等该函数执行完成,返回一个......
  • 在elasticsearch中对象object嵌套的使用
    1.添加映射PUT/object-test/{"mappings":{"properties":{"deviceNo":{"type":"keyword"},"......
  • 序列化器:序列化一个模型对象和多个模型对象
    1.序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串2.反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型......
  • Java 对象和类, 变量类型,构造方法,创建对象,实例,源文件申明规则
    Java作为一种面向对象语言。支持以下基本概念:多态继承封装抽象类对象实例方法重载对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个......