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

news2024/11/27 22:20:02

构造函数

构造函数作用

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

构造函数特点

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2222651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

visual studio设置修改文件字符集方法

该方法来自网文&#xff0c;特此记录备忘。 添加两个组件&#xff0c;分别是Force UTF-8,FileEncoding。 截图如下&#xff1a; 方法如下&#xff1a;vs中点击“扩展”->“管理扩展”&#xff0c;输入utf搜索&#xff0c;安装如下两个插件&#xff0c;然后重启vs&#xf…

Cursor零基础小白教程系列 - 创建你的第一个Cursor 项目

最适合小白零基础的Cursor教程 网站lookai.top相同作者&#xff0c;最新文章会在网站更新&#xff0c;欢迎收藏书签 创建你的第一个Cursor 项目 实操视频 概述 开始使用Cursor进行编程的第一步是创建或导入一个项目。本指南将帮助您了解如何在Cursor中创建新项目、导入现有项…

GPB外链:独立站SEO提升的必备利器!

为什么GPB外链这么受欢迎&#xff1f;这主要是因为它能显著提升网站的权威性和可见性。每一条GPB外链都来自高权重的独立域名&#xff0c;并附有一篇与网站主题高度相关的原创文章。这样的外链&#xff0c;不仅对提升网站排名有帮助&#xff0c;还能为你带来更多的精准流量。 与…

Konva框选移动

效果&#xff0c;可以单独点击控制大小&#xff0c;也可框选控制 代码&#xff1a; <template><div class"rect"><div id"canvas"></div> <!-- 画布容器 --></div> </template><script setup lang"ts&…

【JVM】—G1 GC日志详解

G1 GC日志详解 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点个star~&#x1f60a; 文章目录 G1 GC日志详解1 G1 GC周期2 G1日…

老牌Trans也放量灌水中?年刊文量1000+,网友分享3个月可录,0版面费!

【SciencePub学术】今天给大家推荐的是一本计算机领域的SCI—《IEEE TRANSACTIONS ON ELECTRON DEVICES》&#xff0c;IEEE-Trans系列&#xff0c;虽然常年只能位居2区&#xff0c;不过投稿难度相对较低&#xff0c;所以还是深受大家喜爱的。 期刊概况 IF&#xff1a;2.9 分…

【计算机网络 - 基础问题】每日 3 题(五十四)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

Spring事务的七种传播行为

Spring事务的七种传播行为 1.事务的传播行为是什么&#xff1f;2.具体传播行为2.1 REQUIRED &#xff0c;默认&#xff0c;存在事务则加入该事务&#xff0c;不存在则新建一个事务2.2 REQUIRES_NEW&#xff0c;每次新开启事务&#xff0c;新老事务相互独立2.3 NESTED&#xff0…

ArcGIS无插件加载(无偏移)在线天地图高清影像与街道地图指南

在地理信息系统&#xff08;GIS&#xff09;的应用中&#xff0c;加载高清影像与街道地图对于地图制图、影像查阅、空间数据分析等工作至关重要。天地图作为官方出品的地图服务&#xff0c;以其标准的数据、较快的影像更新速度等特点受到广泛欢迎。以下是如何在ArcGIS中无插件加…

创建项目模版

一 Vite创建Vue3项目 1.1.创建Vue3项目 1.1.1.运行创建项目命令 # 使用 npm npm create vitelatest 1.1.2、填写项目名称 1.1.3、选择前端框架 1.1.4、选择语法类型 1.1.5、按提示运行代码 1.1.6浏览器问 localhost:5173 预览 1.2项目结构 1.2.1vite.config.ts 1.2.2 pac…

【股票】——1-50篇,第一本

一、背景 偶然间在朋友圈看到了雪球花甲老头的公众号&#xff0c;里面的宏观观点和微观观点讲的都太好了&#xff1b;因为作者有那样的经历&#xff0c;所以才能写出来那样的内容&#xff1b;关于股市的&#xff0c;总共500篇&#xff0c;自己也计划系统性的看一看&#xff1b;…

MissingSemester-版本控制系统Git

title: Git的底层及基础使用 date: 2024-05-16 12:00:00 categories: MissingSemester tags: 版本控制系统Git 版本控制系统Git 什么是Git ​ 版本控制系统 (VCSs) 是一类用于追踪源代码&#xff08;或其他文件、文件夹&#xff09;改动的工具。顾名思义&#xff0c;这些工具…

2024年第九期 | CCF ODC《开源战略动态月报》

点击蓝字 关注我们 CCF Opensource Development Committee 导 读 2024年第九期CCF ODC《开源战略动态月报》共摘选33篇文章&#xff0c;分为8个版块&#xff1a;ODC专栏、开源政策、理论观点、产业动态、技术项目、开源组织与机构、开源报告通讯会议以及RISC-V专栏&#xff0c;…

Webserver(1)Linux开发环境搭建

目录 配置软件虚拟机中安装ubuntu安装ubuntu18的操作系统 安装VM tools安装XshellVscode远程连接到虚拟机 配置软件 VMwareVScodeg安装ubuntu 18.04.iso 或者镜像版本 XShellXFTP 虚拟机中安装ubuntu 安装ubuntu18的操作系统 开启虚拟机 选择中文简体 安装VM tools 打开v…

V2X介绍

文章目录 什么是V2XV2X的发展史早期的DSRC后起之秀C-V2XC-V2X 和DSRC 两者的对比 什么是V2X 所谓V2X&#xff0c;与流行的B2B、B2C如出一辙&#xff0c;意为vehicle to everything&#xff0c;即车对外界的信息交换。车联网通过整合全球定位系统&#xff08;GPS&#xff09;导…

Java基础-注解机制详解

文章目录 注解基础Java内置注解内置注解- Override内置注解 - Deprecated内置注解 - SuppressWarnings 元注解元注解 - Target元注解 - Retention & RetentionTarget元注解 - Documented元注解 - Inherited 注解与反射接口自定义注解 深入理解注解Java8提供了哪些新的注解&…

如何高效集成聚水潭数据至MySQL-技术案例解析

如何高效集成聚水潭数据至MySQL-技术案例解析 聚水潭数据集成到MySQL的技术案例分享 在本次技术案例中&#xff0c;我们将探讨如何通过轻易云数据集成平台&#xff0c;将聚水潭的店铺信息高效地集成到MySQL数据库中。具体方案为“聚水潭-店铺信息查询-->BI崛起-店铺信息表”…

NewStarCTF 2023 公开赛道 Web week1-week2

目录 week1 泄漏的秘密 Begin of Upload Begin of HTTP ErrorFlask ​Begin of PHP R!C!E! EasyLogin ​week2 游戏高手 include 0。0 ez_sql ​Unserialize&#xff1f; Upload again! R!!C!!E!! week1 泄漏的秘密 使用ctf-scan.py&#xff08;https://gith…

随身 WiFi 通过 USB 连接路由器共享网络 扩展网络覆盖范围

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 之前几期随身 WiFi 的玩法都是关于骁龙 410 板子的随身 WiFi&#xff0c;可以刷 Debian 之后作为小型家庭服务器&#xff0c;跑跑 Docker&#xff0c;当当打印服务器什么的。而今天介绍的玩法用的是正儿八…

LockBit多版本简单对比

LockBit家族介绍 LockBit是一种非常知名且活跃的勒索软件家族&#xff0c;自2019年首次被发现以来&#xff0c;已经经历了多个版本的演变。它以其高效的加密速度和自动化攻击能力而闻名&#xff0c;对各类组织构成了严重威胁。 发展时间线如下&#xff1a; 时间 事件 2019年…