C++学习——拷贝构造,拷贝赋值,静态变量,单例对象,成员指针

2023-11-05

十七、拷贝构造和拷贝赋值

1.浅拷贝和深拷贝

1)如果类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制了指针本身,而没有复制所指向的内容,这种拷贝方式称为浅拷贝

2)浅拷贝会导致不同的对象之间的数据共享,如果数据在堆区,析构函数还会引发“double free”的错误,导致进程终止,因此必须自己定义一个支持复制指针所指向内容的拷贝构造函数,即深拷贝

参考代码:

#include <iostream>
#include <cstring>
using namespace std;

class String{
public:
    String(const char* str){
        m_str = new char[strlen(str)+1];
        strcpy(m_str,str);
    }
    //析构函数
    ~String(void){
        delete[] m_str;
    }
    //拷贝构造(深拷贝)
    String(const String& that){
        m_str =
            new char[strlen(that.m_str)+1];
        strcpy(m_str,that.m_str);
    }
public:
    const char* c_str(void) const{
        return m_str;
    }
private:
    char* m_str;
};
int main(void)
{
    String s("hello C++!");
    cout << s.c_str() << endl;
    String s2 = s;
    cout << s2.c_str() << endl;
    return 0;
}

2.拷贝赋值

1)当两个对象进行赋值操作时,比如“i3=i2”,编译器会将其处理为“i3.operator=(i2)”成员函数调用形式,该函数被称为拷贝赋值操作符函数,其返回结果是表达式结果

2)如果类中没有定义拷贝赋值函数,那么编译器会提供一个缺省的拷贝赋值函数,但是缺省的拷贝赋值函数和缺省的拷贝构造函数一样,也是浅拷贝,有内存泄露和double free的问题,为了能得到深拷贝的效果,避免问题,必须自己定义深拷贝函数。

	类名& operator=(const 类名& that){
		if(&that != this){//防止自赋值
			//释放旧内存
			//分配新内存
			//拷贝新数据
		}
		return *this;//返回自引用
	}

注:that对应右操作数,this指向左操作数

参考代码:

#include <iostream>
#include <cstring>
using namespace std;

class String{
public:
    //构造函数
    String(const char* str){
        m_str = new char[strlen(str)+1];
        strcpy(m_str,str);
    }
    //析构函数
    ~String(void){
        delete[] m_str;
    }
    //拷贝构造(深拷贝)
    String(const String& that){
        m_str =
            new char[strlen(that.m_str)+1];
        strcpy(m_str,that.m_str);
    }
    String& operator=(const String& that){
        if(&that != this){
            delete[] m_str;
            m_str = new char[
                strlen(that.m_str)+1];
            strcpy(m_str,that.m_str);
            /*char* str = new char[
                strlen(that.m_str)+1];
            delete[] m_str;
            m_str = strcpy(str,that.m_str);
            */
            /*String tmp(that);
            swap(m_str,tmp.m_str);*/
        }
        return *this;
    }
public:
    const char* c_str(void) const{
        return m_str;
    }
private:
    char* m_str;
};
int main(void)
{
    String s("hello C++!");
    cout << s.c_str() << endl;
    String s2 = s;
    cout << s2.c_str() << endl;
    String s3("happy new year!");
    s2 = s3;//拷贝赋值
    cout << s2.c_str() << endl;
    return 0;
}

十八、静态成员

1.静态成员变量

1)语法

class 类名{
    static 数据类型 变量名; //声明
};
数据类型 类名::变量名 = 初值; //定义和初始化

2)普通成员变量属于对象,而静态成员变量不属于对象(计算大小时不算他们)

3)普通成员变量在构造时定义和初始化,而静态成员变量需要在类的外部单独的定义和初始化

4)静态成员变量和全局变量类似,在全局区(数据段),可以把静态成员变量理解成被限制在类中的全局变量

5)使用方法:

类名::静态成员变量名; //推荐
对象.静态成员变量名;//和上面等价

参考代码:

#include <iostream>
using namespace std;

class A{
public:
    //普通成员变量在构造时定义和初始化
    A(int data):m_data(data){}
    int m_data;
    static int s_data;
};
//静态成员变量需要在类的外部单独定义和初始化
int A::s_data = 20;

int main(void)
{
    A a1(10);
    cout << "sizeof(a1):" << sizeof(a1) 
        << endl;//4
    cout << a1.m_data << endl;//10
    cout << A::s_data << endl;//20
    cout << a1.s_data << endl;//20

    A a2(10);
    a2.m_data = 11;
    a2.s_data = 22;

    cout << a1.m_data << endl;//10
    cout << a1.s_data << endl;//22

    return 0;

}

2.静态成员函数

1)语法

class 类名{
    static 返回类型 函数名(形参表){......}
};

2)静态成员函数没有this指针,也没有const属性

3)使用方法(静态成员函数的调用可以不依赖对象)

类名::静态成员函数(实参表); //推荐
对象.静态成员函数(实参表); //和上面等价

注:在静态成员函数中只能访问静态成员,在非静态成员函数中既可以访问静态成员,也可以访问非静态成员

参考代码:

#include <iostream>
using namespace std;

class A{
    //普通成员函数在构造时定义和初始化
    A(int data):m_data(data){}
    static void func1(void)
    {
        cout << "静态成员变量" << endl;
        //cout << m_data << endl;
        cout << s_data << endl;
     }
     void func2(void){
        cout << "非静态成员函数" << endl;
        cout << m_data << endl;
        cout << s_data << endl;
      }
    int m_data;
    static int s_data;
};
//静态成员变量需要在类的外部单独定义和初始化
int A::s_data = 20;
int main()
{
    A::func1();
    A a1(10);
    a1.func2();
}

3.单例模式

1)概念:一个类只允许存在唯一的对象,并提供它访问的方法

2)实现单例模式的方法

①禁止在类的外部创建对象:私有化构造函数(包括拷贝构造)

②类的内部维护唯一的对象:静态成员变量(如果不是静态的就会出现递归创建,而无法确定类的大小)

③提供单例对象的访问方法:静态成员函数(由于非静态成员函数只有在类存在的时候才能使用,但是单例模式下,无法创建对象,所以不可以使用非静态成员函数)

class A{
public:
    A& get()
    {
        return a;
    }
private:
    A(void);
    A(const A& );
    int m_data;
    static A a;
};

3)创建方式

①饿汉式:单例对象无论用或不用,程序启动即创建

②懒汉式:单例对象用时再创建

参考代码:

饿汉式

//单例模式:饿汉式
#include <iostream>
using namespace std;

class Singleton{
public:
    //3)使用静态成员函数获取单例对象
    static Singleton& getInstance(void){
        return s_instance;
    }
    void print(void)const{
        cout << m_data << endl;
    }
private:
    //1)私有化构造函数(包括拷贝构造)
    Singleton(int data):m_data(data){
        cout << "单例对象被创建了" << endl;
    }
    Singleton(const Singleton&);
private:
    int m_data;
    //2)使用静态成员变量维护单例对象
    static Singleton s_instance;
};
Singleton Singleton::s_instance(1234); 
int main(void)
{
    cout << "main函数开始执行.." << endl;
    Singleton& s1=Singleton::getInstance();
    Singleton& s2=Singleton::getInstance();
    Singleton& s3=Singleton::getInstance();
    cout << "&s1=" << &s1 << endl;
    cout << "&s2=" << &s2 << endl;
    cout << "&s3=" << &s3 << endl;
    s1.print();
    s2.print();
    s3.print();

    //Singleton s4(4321);//应该error
    //Singleton s4 = s1;//应该error
    return 0;
}

懒汉式:

//单例模式:懒汉式
#include <iostream>
using namespace std;

class Singleton{
public:
    //3)使用静态成员函数获取单例对象
    static Singleton& getInstance(void){
        if(s_instance == NULL){
            s_instance=new Singleton(1234);
        }
        ++s_count;
        return *s_instance;
    }
    void print(void)const{
        cout << m_data << endl;
    }
    //单例对象不用时即销毁,什么时候不用?
    //最后一个使用者不用时再销毁。
    void release(void){
        if(--s_count == 0){
            delete s_instance;
            s_instance = NULL;
        }
    }
private:
    //1)私有化构造函数(包括拷贝构造)
    Singleton(int data):m_data(data){
        cout << "单例对象被创建了" << endl;
    }
    ~Singleton(void){
        cout << "单例对象被销毁了" << endl;
    }
    Singleton(const Singleton&);
private:
    int m_data;
    //2)使用静态成员变量维护单例对象
    static Singleton* s_instance;
    //计数:记录单例对象使用者的个数
    static int s_count;
};
Singleton* Singleton::s_instance = NULL; 
int Singleton::s_count = 0;

int main(void)
{
    cout << "main函数开始执行.." << endl;
    //++s_count ==> 1 
    Singleton& s1=Singleton::getInstance();
    //++s_count ==> 2 
    Singleton& s2=Singleton::getInstance();
    //++s_count ==> 3 
    Singleton& s3=Singleton::getInstance();
    cout << "&s1=" << &s1 << endl;
    cout << "&s2=" << &s2 << endl;
    cout << "&s3=" << &s3 << endl;
    s1.print();
    s1.release();//--s_count ==> 2
    
    s2.print();
    s3.print();

    s2.release();//--s_count ==> 1
    s3.release();//--s_count ==> 0,delete

    return 0;
}

十九、成员指针

1.成员变量指针

1)定义

类型 类名::*成员指针变量名 = &类名::成员变量

2)使用

对象.*成员指针变量名;
注:“.*”被称为直接成员指针解引用操作符

对象指针->*成员指针变量名;
注:“->*”被称为间接成员指针解引用操作符

#include <iostream>
#include <cstdio>
using namespace std;
class Student{
public:
    Student(const string& name)
        :m_name(name){}
    int m_no;
    int m_age;
    string m_name;
};
int main(void)
{
    string Student::*pname=&Student::m_name;

    Student s1("杨健");
    Student* s2 = new Student("王建立");

    cout << s1.*pname << endl;
    cout << s2->*pname << endl;
    delete s2;
    s2 = NULL;

    //成员变量地址=
    //对象地址+成员变量指针保存的相对地址
    printf("pname=%p\n",pname);
    printf("&s1.m_name=%p\n",&s1.m_name);
    printf("&s1=%p\n",&s1);

    return 0;
}

2.成员函数指针

1)定义

返回类型 (类名::*成员函数指针)(形参表) = &类名::成员函数名;

2)使用

(对象.*成员函数指针)(实参表);
(对象指针->*成员函数指针)(实参表);

参考代码:

#include <iostream>
#include <cstdio>
using namespace std;
class Student{
public:
    Student(const string& name)
        :m_name(name){}
    void who(void){
        cout << "我叫" << m_name << endl;
    }
    string m_name;
};
int main(void)
{
    void (Student::*pwho)() = &Student::who;
    Student s1("杨健");
    Student* s2 = new Student("王建立");
    (s1.*pwho)();
    (s2->*pwho)();
    
    return 0;
}

 

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++学习——拷贝构造,拷贝赋值,静态变量,单例对象,成员指针 的相关文章

  • WindowsError:[错误 126] 使用 ctypes 加载操作系统时

    python代码无法在Windows 7平台上运行 def libSO lib ctypes cdll LoadLibrary ConsoleApplication2 so lib cfoo2 1 3 当我尝试运行它时 得到来自python
  • 为什么这个 Web api 控制器不并发?

    我有一个 Web API 控制器 里面有以下方法 public string Tester Thread Sleep 2000 return OK 当我调用它 10 次 使用 Fiddler 时 我预计所有 10 次调用都会在大约 2 秒后
  • 使用 CMake 时如何导出 Emscripten 中的 C 函数

    In 本教程 https emscripten org docs porting connecting cpp and javascript Interacting with code html interacting with code
  • Grpc - 将消息从一个客户端发送到连接到同一服务器的另一个客户端

    是否可以将消息从一个客户端发送到连接到同一服务器的另一个客户端 我想将数据从一个客户端发送到服务器然后发送到特定客户端 我想我需要获取客户端 ID 但我不知道如何获取此 ID 以及如何从服务器将此消息发送到该客户端 我这里有一个样本 这是一
  • 转换 const void*

    我有一个函数返回一个const void 我想用它的信息作为char 我可以将它投射为 C 风格的罚款 char variable但是当我尝试使用reinterpret cast like reinterpret cast
  • 如何在类文件中使用 Url.Action() ?

    如何在 MVC 项目的类文件中使用 Url Action Like namespace 3harf public class myFunction public static void CheckUserAdminPanelPermissi
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • 前向声明类型和“已声明为类类型的非类类型”

    我对以下代码有问题 template
  • MVC3中设置下拉列表中的所选项目

    我必须为视图中的下拉列表设置所选项目 但它不起作用 View div class editor label Html LabelFor model gt model Gender div div class editor field Htm
  • 如何将 SOLID 原则应用到现有项目中

    我对这个问题的主观性表示歉意 但我有点卡住了 我希望之前处理过这个问题的人能够提供一些指导和建议 我有 现在已经成为 一个用 C 2 0 编写的非常大的 RESTful API 项目 并且我的一些类已经变得巨大 我的主要 API 类就是一个
  • 如何将AVFrame转换为glTexImage2D使用的纹理?

    如您所知 AVFrame 有 2 个属性 pFrame gt data pFrame gt linesize 当我从视频 sdcard test mp4 android平台 读取帧后 并将其转换为RGB AVFrame副 img conve
  • 从网页运行 ClickOnce 应用程序,无需用户操作

    我们有一个基于 Java 的 Web 应用程序以及用 C 编写的相同应用程序 如果 java 检查器发现客户端计算机上没有安装 Java 则应该运行该应用程序 这个想法是运行 C 单击一次 http en wikipedia org wik
  • 已发布的 .Net Core 应用程序警告安装 .Net Core,但它已安装

    我制作了一个 WPF 和控制台应用程序 供某人在我无法访问的私人服务器上使用 我使用 Visual Studio 2019 的内置 发布向导 来创建依赖于框架的单文件应用程序 当该人打开 WPF 应用程序时 他们会看到标准警告 他们单击 是
  • 不可变类与结构

    以下是类与 C 中的结构的唯一区别 如果我错了 请纠正我 类变量是引用 而结构变量是值 因此在赋值和参数传递中复制结构的整个值 类变量是存储在堆栈上的指针 指向堆上的内存 而结构变量作为值存储在堆上 假设我有一个不可变的结构 该结构的字段一
  • 在 C 中使用枚举而不是 #defines 作为编译时常量是否合理?

    在 C 工作了一段时间后 我将回到 C 开发领域 我已经意识到 在不必要的时候应该避免使用宏 以便让编译器在编译时为您做更多的工作 因此 对于常量值 在 C 中我将使用静态 const 变量或 C 11 枚举类来实现良好的作用域 在 C 中
  • memcpy/memmove 到联合成员,这是否设置“活动”成员?

    重要说明 一些评论者似乎认为我是从工会抄袭的 仔细看memcpy 它从普通旧地址复制uint32 t 它不包含在联合中 另外 我正在复制 通过memcpy 到工会的特定成员 u a16 or u x in a union 不直接到整个联盟本
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 使动态创建的链接标签在 Winforms 中可点击

    我正在制作一个程序 允许用户单击由动态链接标签创建的公司名称 在我想知道如何做到这一点之前 我从未在 C 中使用过链接标签 可为特定用户生成的业务数量各不相同 因此每个用户的链接标签数量并不相同 然后我想捕获业务 ID 以进行 Json 调
  • Visual Studio 2015 - Web 项目上缺少共享项目参考选项卡

    我从 MSDN 订阅升级到 Visual Studio 2015 因为我非常兴奋地阅读有关共享项目的信息 当我们想要做的只是重用代码时 不再需要在依赖项中管理 21382 个 nuget 包 所以我构建了一个测试共享项目 其中包含一些代码
  • C++:二叉树所有节点值的总和

    我正在准备面试 我被一个二叉树问题困住了 我们如何计算二叉树所有节点中存在的值的总和 优雅的递归解决方案 伪代码 def sum node if node NULL return 0 return node gt value sum nod

随机推荐

  • 图片加载 预制体加载 视频加载 文字加载

    using System Collections using System Collections Generic using UnityEngine using UnityEngine UI using RenderHeads Media
  • deque相关用法

    1 queue的相关用法 include
  • 值得收藏的25道Python练手题(附详细答案)

    题目 1 水仙花数 水仙花数 Narcissistic number 也被称为超完全数字不变数 pluperfect digital invariant PPDI 自恋数 自幂数 阿姆斯壮数或阿姆斯特朗数 Armstrong number
  • 按键控制LED闪烁实验

    实验任务 本节实验任务是使用底板上的PL KEY0和PL KEY1按键来控制底板上的PL LED0和PL LED1两个LED的闪烁方式 没有按键按下时 两个LED保持常亮 如果按键0按下 则两个LED交替闪烁 如果按键1按下 则两个LED同
  • shell脚本系列:1、shell、bash和shell脚本

    shell脚本系列 1 shell bash和shell脚本 文章目录 shell脚本系列 1 shell bash和shell脚本 1 前言 2 shell Bash shell script简介 2 1 shell 2 2 Bash 2
  • Ubuntu安装qt 5.12

    1 下载qt5 12 10 qt下载网址 https download qt io archive qt 下载完成后 将下载的文件拷入Ubuntu 去到文件目录添加执行权限 chmod x 文件名 2 安装 运行该文件 如 qt opens
  • 《遥感原理与应用》总结——遥感图像自动识别分类

    目录 遥感图像自动识别分类 1 基础知识 2 特征变换及特征选择 3 监督分类 4 非监督分类 5 非监督分类与监督分类的结合 6 分类后处理和误差分析 7 提高分类精度的方法 8 基于目标的信息提取 遥感图像自动识别分类 遥感图像自动分类
  • BCGSoft Demo示例展示:Ribbon示例集合

    BCGSoft公司的BCGControlBar产品是全球最优秀的MFC界面类库 功能强大 显示丰富 在国际上享有盛誉 并屡次获奖 是VC界面设计的必备首选 本文中的这些示例程序主要演示了如何在MDI和SDI应用程序中使用Ribbon控件和R
  • 力扣468验证IP地址C++判断合法IP字符串

    目录 前言 题目描述 解题思路 主功能函数分类大框架 判断IPv4是否合法 判断IPv6是否合法 其余小边界条件 调试后得 完整代码 前言 这是一道常见的笔试面试题 我找实习已经碰到两次了 和矩阵的乘法出现频率一样高 你校招要是全程没遇到可
  • 科技云报道:AI+云计算共生共长,能否解锁下一个高增长空间?

    科技云报道原创 在过去近一年的时间里 AI大模型从最初的框架构建 逐步走到落地阶段 然而 随着AI大模型深入到千行百业中 市场开始意识到通用大模型虽然功能强大 但似乎并不能完全满足不同企业的个性化需求 大模型技术的安全性 解释性 易用性等综
  • 刚打开虚拟机后出现无法连接网络问题的解决办法 ping name or service not known

    在VMware WorkStation中创建了一个新的Linux CentOS7虚拟机后 发现无法连接网络 ping不通 ping www baidu com显示 ping name or service not known 经高人指点后
  • SonarQube之Mysql安装

    Mysql 从5 1升级到5 6安装 转 https www cnblogs com 007sx p 7083143 html 一 检查系统是否安装其他版本的MYSQL数据 yum list installed grep mysql yum
  • 四种常用的容器(ArrayList,LinkedList,HashSet,HashMap)的基本方法和基本概念

    1 小王有很多1毛 2毛 5毛的旧钞票 现在想分类并清点一下每种钞票有多少张 一共有多少张 请用HashMap建立数据结构 帮他一起清点吧 程序的输入 通过键盘输入若干行 每一行代表某一张旧钞票的面额 直到输入为 1为止 程序的输出 打印每
  • 知识图谱构建技术一览 #CSDN博文精选# #高效学习法# #系统化学习# #IT技术学习#

    大家好 我是小C 又见面啦 文章过滤器 精选大咖干货 助力学习之路 5天20篇CSDN精选博文带你掌握系统化学习方法 专栏将挑选有关 系统化学习方法 的20篇优质文章 帮助大家掌握更加科学的学习方法 在这里 你将收获 快速掌握系统化学习的理
  • 【C语言】希尔排序

    一 算法描述 希尔排序是直接插入排序的升级版 相隔相同步长的进行插入排序 每次循环中步长减半 直到步长为1 二 希尔排序代码 void shell int p int n int step temp 0 i j for step n 2 s
  • RocketMQ学习笔记(实操篇)

    目录 基本操作 启动 测试 双主双从集群搭建 总体架构 工作流程 服务器环境 Host添加信息 防火墙配置 环境变量配置 创建消息存储路径 broker配置文件 修改启动脚本文件 服务启动 查看进程状态 查看日志 mqadmin管理工具 使
  • Java读写Excel

    一 Excel基本概念 Workbook 工作簿 代表一个Excel文件 Excel分为两种 后缀名为xls的HSSFWorkBook 2003版本及以前 和后缀名为xlsx的XSSFWorkBook 2007版本及以后 Sheet 表格
  • 解决vscode中不能使用yarn命令

    由于vscode中的集成终端使用的是powershell 所以我们要设置一下powershell的执行权限 解决方法 进入C Windows System32 WindowsPowerShell v1 0目录 找到powershell ex
  • Python使用defaultdict解决字典默认值的问题

    文章目录 1 导入defaultdict 2 创建defaultdict 3 使用defaultdict 4 添加defaultdict默认值 5 结论 在Python中 defaultdict是一种特殊类型的字典 它可以自动为字典中不存在
  • C++学习——拷贝构造,拷贝赋值,静态变量,单例对象,成员指针

    十七 拷贝构造和拷贝赋值 1 浅拷贝和深拷贝 1 如果类中包含指针形式的成员变量 缺省的拷贝构造函数只是复制了指针本身 而没有复制所指向的内容 这种拷贝方式称为浅拷贝 2 浅拷贝会导致不同的对象之间的数据共享 如果数据在堆区 析构函数还会引