首页 > 编程语言 >C++学习路线(二十二)

C++学习路线(二十二)

时间:2024-10-24 17:52:09浏览次数:3  
标签:二十二 std C++ 路线 Person address 拷贝 include 构造函数

构造函数

构造函数作用

在创建一个新的对象时,自动调用的函数,用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。

构造函数特点

1.自动调用(在创建新对象时,自动调用)

2.构造函数的函数名,和类名相同

3.构造函数没有返回类型

4.可以有多个构造函数(即函数重载形式)

构造函数种类

默认构造函数

自定义的构造函数

拷贝构造函数

赋值构造函数

默认构造函数

没有参数的构造函数,称为默认构造函数。

合成的默认构造函数

但没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数

1)如果数据成员使用了“类内初始值”,就使用这个值来初始化数据成员。

2)否则,就使用默认初始化(实际上,不做任何初始化)

#include <iostream>
using namespace std;

class Human {
public:
	Human(){
		cout << "Human constructor called." << endl;
	}
};
int main() {
	Human p1;
	return 0;
}

只要手动定义了任何一个构造函数,编译器就不会生成“合成的默认构造函数”一般情况下,都应该定义自己的构造函数,不要使用“合成的默认构造函数”【仅当数据成员全部使用了“类内初始值”,才宜使用“合成的默认构造函数”】 

赋值构造函数

#include <iostream>
using namespace std;

class Person {
public:
    void setAddr(char* p) {
        addr = p;
    }
    void description() const {
        cout << "Address: " << addr << endl;
    }
private:
    char* addr;
};

int main() {
    char* str = new char[100] {"123 Main St"};
    Person p1;
    p1.setAddr(str);
    Person p2;
    p2 = p1; // 执行浅拷贝
    p1.description(); // 应该打印: Address: 123 Main St
    p2.description(); // 应该也打印: Address: 123 Main St
    // 修改原始字符串,并查看 p1 和 p2 是否受到影响
    strncpy_s(str,100 ,  "456 Elm St" , _TRUNCATE );
    p1.description(); // 现在打印: Address: 456 Elm St
    p2.description(); // 也打印: Address: 456 Elm St
    delete[] str; // 记得删除动态分配的内存

    return 0;
}

你执行 p2 = p1; 这一行时,默认情况下编译器会使用浅拷贝(shallow copy)。这意味着如果你的类中有指针或者持有对其他资源的引用的话,那么这些资源会被同样的引用到新的对象中去,而不是创建独立的副本。

我们可以自定义运算符重载Person& operator=(const Person& person)来实现深拷贝

#include <iostream>
using namespace std;

class Person {
public:
    void setAddr(char* p) {
        addr = p;
    }
    void description() const {
        cout << "Address: " << addr << endl;
    }
    Person& operator=(const Person& p) {
        //防止对象自身赋值
        if (this == &p) return *this;

        //如果执行f2 = f1
        //就会调用f2.operator=(f1)

        //如果有必要 先释放自己的动态资源
        delete[] addr;
        //再拷贝p的动态资源到自己  
        addr = new char[strlen(p.addr) + 1];
        strcpy_s(addr, strlen(p.addr) + 1, p.addr);
        //返回对象本身的引用,是为了方便能够链式处理
        //f1 = f2 = f3
        return *this;
    }
    ~Person() {
        delete[] addr;
    }
private:
    char* addr;
};

int main() {
    char* str = new char[100] {"123 Main St"};
    Person p1;
    p1.setAddr(str);
    Person p2;
    p2 = p1; // 执行浅拷贝
    p1.description(); // 应该打印: Address: 123 Main St
    p2.description(); // 应该也打印: Address: 123 Main St
    // 修改原始字符串,并查看 p1 和 p2 是否受到影响
    strncpy_s(str,100 ,  "456 Elm St" , _TRUNCATE );
    p1.description(); // 现在打印: Address: 456 Elm St
    p2.description(); // 也打印: Address: 456 Elm St
    delete[] str; // 记得删除动态分配的内存

    return 0;
}

拷贝构造函数

#include <iostream>
using namespace std;

class Person {
public:
    Person() {
        age = 0;
        name = "";
    }
    Person(const Person& p) {
        cout << "Copy constructor called." << endl;
        age = p.age;
        name = p.name;
    }
    void setAge(int a) {
        age = a;
    }
    void setName(string n) {
        name = n;
    }
private:
    int age;
    string name;
};

int main() {
    Person p1;
    p1.setAge(25);
    p1.setName("John");
    Person p2(p1);
    Person p3 = p1;
    return 0;
}

合成的拷贝构造函数

        是指当类中没有显式定义拷贝构造函数时,编译器自动为该类生成的一个默认拷贝构造函数。这个自动生成的拷贝构造函数会执行成员变量的浅拷贝(shallow copy),即将源对象的每个成员变量的值直接复制到新创建的对象中。 简单来说,合并的拷贝构造函数是编译器在类定义中没有显式提供拷贝构造函数时自动提供的一个默认实现。这个默认实现会逐成员地复制源对象的值到新对象中,但它不会处理动态分配的内存(如指针指向的内存)的深拷贝(deep copy)问题,这可能会导致资源泄露或双重释放等问题。

合成的拷贝构造函数都是浅拷贝 下面给出一个浅拷贝的例子。

#include <iostream>
using namespace std;

class Person {
public:
	void mallocAdress() {
		address = new char[100];
	}
	~Person() {
		if(address) delete[] address;
	}
	void print() {
		printf("address: %p\n", address);
	}
private:
	char* address = nullptr;
};

int main() {
	Person p1;
	p1.mallocAdress();
	Person p2 = p1;
	Person p3(p1);
	p1.print();
	p2.print();
	return 0;
}

要解决合并的拷贝函数,我们可以自己定义一个拷贝构造函数

#include <iostream>
using namespace std;

class Person {
public:
	void mallocAdress() {
		address = new char[100];
	}
	~Person() {
		if(address) delete[] address;
	}
	void print() {
		printf("address: %p\n", address);
	}
	Person(const Person& p) {
		address = new char[100];
	}
private:
	char* address = nullptr;
};

int main() {
	Person p1;
	p1.mallocAdress();
	Person p2 = p1;
	Person p3(p1);
	p1.print();
	p2.print();
	return 0;
}
什么时候调用拷贝构造函数

1.调用函数时,实参是对象,形参不是引用类型

#include <iostream>
using namespace std;

class MyClass {
public:
	MyClass(int val) {
		this->val = val;
		cout << "Constructor called" << endl;
	}
	MyClass(const MyClass& obj) {
		cout << "Copy constructor called" << endl;
		this->val = obj.val;
	}
	int val;
};

void func(MyClass obj) {
	cout << "Value of obj is " << obj.val << endl;
}

int main() {
	MyClass obj1(10);
	func(obj1);
	return 0;
}

2.函数的返回类型是类,而且不是引用类型

#include <iostream>

class MyClass {
public:
    MyClass(int val) : value(val) {
        std::cout << "MyClass constructor called." << std::endl;
    }
    MyClass(const MyClass& other) : value(other.value) {
        std::cout << "Copy constructor called." << std::endl;
    }
    int value;
};
MyClass returnObject() {
    MyClass temp(20);
    return temp; // 返回局部变量会调用拷贝构造函数
}
int main() {
    MyClass returned = returnObject(); // 这里也会调用拷贝构造函数
    std::cout << "Returned object value: " << returned.value << std::endl;
    return 0;
}

虽然会因为RVO优化,不调用MyClass的拷贝构造函数

3.对象数组的初始化列表中,使用对象。

#include <iostream>

class MyClass {
public:
    MyClass(int val) : value(val) {
        std::cout << "MyClass constructor called." << std::endl;
    }
    MyClass(const MyClass& other) : value(other.value) {
        std::cout << "Copy constructor called." << std::endl;
    }
    int value;
};

int main() {
    MyClass source(30);
    MyClass array[3] = { source, source, source }; // 这里会三次调用拷贝构造函数
    for (int i = 0; i < 3; ++i) {
        std::cout << "Array[" << i << "] value: " << array[i].value << std::endl;
    }
    return 0;
}

类和基本数据不同(int , float , double , long , long long)

类的构成:方法和数据

#include <iostream>
using namespace std;
class Human {
public:
    int age;
    string name;
    void print() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
    Human() {
        cout << "Constructor called." << endl;
    }
    Human(const Human& h) {
        cout << "Copy constructor called." << endl;
    }
    Human& operator=(const Human& h) {
        cout << "Assignment operator called." << endl;
        return *this;
    }
    ~Human() {
        cout << "Destructor called." << endl;
    }
};
int main() {
    Human h1;
    cout << "---" << endl;
    Human* h2 = &h1;
    return 0;
}

上面以指针的形式来进行初始化的话不会调用拷贝

如果某数据成员使用类内初始值,同时又在构造函数中进行了初始化

那么以构造函数中的初始化为准

相当于构造函数中的初始化,会覆盖对应的类内初始值

#include <iostream>

class MyClass {
public:
    MyClass() : value(10) { // 类内初始化
        std::cout << "Default constructor called." << std::endl;
    }

    MyClass(int val) : value(val) { // 构造函数初始化
        std::cout << "Constructor with initial value called." << std::endl;
    }

    MyClass(const MyClass& other) : value(other.value) { // 拷贝构造函数
        std::cout << "Copy constructor called." << std::endl;
    }

    void printValue() const {
        std::cout << "Value is: " << value << std::endl;
    }

private:
    int value = 5; // 类内初始值
};

int main() {
    MyClass obj1; // 使用类内默认构造函数
    obj1.printValue(); // 输出 Value is: 10

    MyClass obj2(20); // 使用构造函数初始化
    obj2.printValue(); // 输出 Value is: 20

    return 0;
}

标签:二十二,std,C++,路线,Person,address,拷贝,include,构造函数
From: https://blog.csdn.net/weixin_45169583/article/details/143206429

相关文章

  • 计算机毕业设计项目推荐:大学生实习成绩评价系统的设计与实现38147(开题答辩+程序定制+
    摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对大学生实习成绩......
  • 【C++】红黑树万字详解(一文彻底搞懂红黑树的底层逻辑)
    目录00.引入01.红黑树的性质02.红黑树的定义03.红黑树的插入1.按照二叉搜索树的规则插入新节点2.检测新节点插入后,是否满足红黑树的性质1.uncle节点存在且为红色2.uncle节点不存在3.uncle节点存在且为黑色 04.验证红黑树00.引入和AVL树一样,红黑树也是一种自平......
  • 计算机毕业设计项目推荐,个人知识管理系统 79004(开题答辩+程序定制+全套文案 )上万套实
    摘 要尽管我们每天面临的信息越来越多,信息过载与信息噪音越来越严重,但只要我们能充分利用个人知识管理技能,借助有效的个人知识管理软件相信战胜海量信息不再是困难。本课题在分析了个人知识管理现状以及对现有的个人知识管理网站进行研究比较的基础上,针对网络交流互助的特......
  • C++Socket通讯样例(服务端)
    1.创建Socket实例并开启。privateintOpenTcp(intport,stringip=""){//1.开启服务端try{_tcpServer=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);IPAddressipAddr=IPAddress.Any;......
  • C++11新特性:lambda表达式
    lambda表达式目录lambda表达式常见lambda表达式的省略式值的捕获lambda表达式的类型使用场景简述附:lambda的常量性......
  • 20个基础到进阶版的OpenCV4.9.0趣味项目(C++版)(八)——石头、剪刀、布识别手势识别(传统方
    20个基础到进阶版的OpenCV4.9.0趣味项目(C++版)(八)——石头、剪刀、布识别手势识别(传统方法)文章目录20个基础到进阶版的OpenCV4.9.0趣味项目(C++版)(八)——石头、剪刀、布识别手势识别(传统方法)一、引言二、核心知识1.YCrCb空间转换和提取1)YCrCb色彩空间:2)分割:2.凸包凸包计算......
  • 大话C++:第23篇 输入输出
    1输入输出概述C++输入输出(I/O)是C++编程语言中非常重要的一部分,它涉及到从外部设备(如键盘、文件等)读取数据以及将数据写入到这些设备中。C++提供了一套丰富的I/O库,程序员可以使用这些库来执行各种输入输出操作。C++的I/O操作主要依赖于<iostream>头文件,它定义了用于输入输出......
  • Qt/C++路径轨迹回放/回放每个点信号/回放结束信号/拿到移动的坐标点经纬度
    一、前言说明在使用百度地图的路书功能中,并没有提供移动的信号以及移动结束的信号,但是很多时候都期望拿到移动的哪里了以及移动结束的信号,以便做出对应的处理,比如结束后需要触发一些对应的操作。经过搜索发现很多人都有这个需求,需要在js文件中加上一点代码才行,也就是在start开始......
  • [C++]在windows基于C++编程署yolov11-pose的openvino姿态估计模型cmake项目部署演示源
    【算法介绍】在Windows系统上,基于C++编程部署YOLOv11-Pose的OpenVINO姿态估计模型,可以通过CMake项目来实现。以下是简要介绍:首先,需要准备开发环境,包括安装OpenVINOToolkit、CMake、OpenCV和C++编译器(如GCC或MSVC)。OpenVINO是英特尔开发的一款用于优化和部署深度学习模型的工具套件......
  • [C++]在windows基于C++编程署yolov11-cls的openvino图像分类模型cmake项目部署演示源
    【算法介绍】在Windows系统上,基于C++编程部署YOLOv11-CLS的OpenVINO图像分类模型,可以通过CMake项目来实现。以下是简要介绍:首先,需要准备开发环境,包括安装OpenVINOToolkit、CMake、OpenCV和C++编译器(如GCC或MSVC)。OpenVINO是英特尔开发的一款用于优化和部署深度学习模型的工具套件,......