我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++中的对象初始化操作代码

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++中的对象初始化操作代码

当对象在创建时获得了一个特定的值,我们说这个对象被初始化。初始化不是赋值,初始化的含义是创建变量赋予其一个初始值,而赋值的含义是把当前值擦除,而以一个新值来替代。对象初始化可以分为默认初始化、直接初始化、拷贝初始化以及值初始化。


// new edit on 2020.7.23
#pragma once
#include <iostream>
using namespace std;

class ClassTest {
 public:

  //定义默认构造函数
  ClassTest()  
  {
    c[0] = '\0';
    cout << "1) ClassTest()" << endl;
  }

  // 直接初始化
  ClassTest(const char* pc)  
  {
    strcpy_s(c, pc);
    cout << "2) ClassTest (const char *pc)" << endl;
  }

  //复制/拷贝构造函数
  ClassTest(const ClassTest& ct)  
  {
    strcpy_s(c, ct.c);
    cout << "3) ClassTest(const ClassTest& ct)" << endl;
  }

  //重载赋值操作符
  ClassTest& operator=(const ClassTest& ct)  
  {
    strcpy_s(c, ct.c);
    cout << "4) ClassTest& operator=(const ClassTest &ct)" << endl;
    return *this;
  }

 private:
  char c[256];
};

ClassTest func(ClassTest temp) { return temp; }

int demo_test() {
  cout << "ct1: ";
  ClassTest ct1("ab");  // 直接初始化
  cout << "ct2: ";
  ClassTest ct2 = "ab"; // 直接初始化

  
  cout << "ct3: ";
  ClassTest ct3 = ct1;  // 复制初始化
  cout << "ct4: ";
  ClassTest ct4(ct1);   // 复制初始化
  cout << "ct5: ";
  ClassTest ct5 = ClassTest();  // 默认构造函数
  
  cout << "\nct6: "; // 依次調用 1)、2)、4),即默认、直接、重载
  ClassTest ct6;  
  ct6 = "caoyan is a good boy!"; 

  cout << "\nct7: ";  
  ClassTest ct7;  // 依次調用 1)、3)、3)、4)
  ct7 = func(ct6);

  return 0;
}

old code:


// (1)默认初始化
int i1;//默认初始化,在函数体之外(初始化为0)  


int f(void)  
{  
int i2;//不被初始化,如果使用此对象则报错  
}  

string empty;//empty非显示的初始化为一个空串,调用的是默认构造函数  

// (2)拷贝初始化
string str1(10,'9');//直接初始化  
string str2(str1);//直接初始化  
string str3 = str1;//拷贝初始化  

 // (3)值初始化
vector<int> v1(10);//10个元素,每个元素的初始化为0  
vector<string> v2(10);//10个元素,每个元素都为空   

int *pi = new int;//pi指向一个动态分配的,未初始化的无名对象  
string *ps = new string;//初始化为空string  
int *pi = new int;//pi指向一个未初始化的int  

int *pi = new int(1024);//pi指向的对象的值为1024  
string *ps = new string(10,'9');/
  cout << "ct3: ";
  ClassTest ct3 = ct1; //复制初始化
  cout << "ct4: ";
  ClassTest ct4(ct1); //直接初始化
  cout << "ct5: ";
  ClassTest ct5 = ClassTest(); //复制初始化
  cout << "ct6: ";
  ClassTest ct6; //复制初始化
  ct6 = "caoyan is a good boy!";
  cout << "ct7: ";
  ClassTest ct7;
  ct7 = func(ct6);
  return 0;
}

测试结果:

我们可以看到,比较复杂的是ct6和ct7,其中ct6还是比较好理解的,ct7这种情况比较难懂,为什么会有两个拷贝构造函数的调用????

第一次拷贝构造函数的调用:第一次很简单,是因为函数参数的传递,将ct6作为参数传递给temp,用ct6的值初始化temp会调用拷贝构造函数;

第二次拷贝构造函数的调用:因为要返回一个ClassTest对象,我们的编译器怎么做????首先它将temp对象拷贝到func函数的上一级栈帧中,它的上一级栈帧是main函数的栈帧,那么当函数返回时,参数出栈,temp对象的内存空间就会被收回,但是它的值已经被拷贝到main栈帧的一个预留空间中,所以从temp到预留空间的拷贝也是调用拷贝构造函数,最后一步就是给ct7赋值,毫无疑问调用赋值构造函数;对栈帧不同的同学可以看看《程序员的自我修养》一书,里面讲得很详细!

2、初始化列表、构造函数与=赋值之间的区别

总所周知,C++对象在创建之时,会由构造函数进行一系列的初始化工作。以没有继承关系的单个类来看,除了构造函数本身的产生与指定,还涉及到初始化步骤,以及成员初始化方式等一些细节,本篇笔记主要对这些细节进行介绍,弄清C++对象在初始化过程中一些基本运行规则。

构造函数指定

通常,我们在设计一个类的时候,会为这个类编写对应的default constructor、copy constructor、copy assignment operator,还有一个deconstructor。即便我们仅仅编写一个空类,编译器在编译时仍旧会为其默认声明一个default constructor、copy constructor、copy assignment operator与deconstructor,如果在代码里面存在着它们的使用场景,那么这个时候编译器才会创建它们。

class MyCppClass {}

一旦我们为一个类编写了default constructor,那么编译器也就不会为其默认生成default constructor,对于其他几个函数也一样。对于编译器默认生成的constructor来说,它会以一定规则对每一个数据成员进行初始化。考虑到成员初始化的重要性,在编写自己的constructor时就需要严谨认真了,特别是在类的派生与继承情况下这点显得尤为重要。对于copy constructor和assignment operator的运用场景,这里不得不多说一点,见如下代码:


#include <iostream>
 
using std::cout;
using std::endl;
 
class MyCppClass
{
public:
    MyCppClass()
    {
        std::cout <<"In Default Constructor!" <<std::endl;
    }
 
    MyCppClass(const MyCppClass& rhs)
    {
        std::cout <<"In Copy Constructor!" <<std::endl;
    }
 
    MyCppClass& operator= (const MyCppClass& rhs)
    {
        std::cout <<"In Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
int main()
{
    MyCppClass testClass1;                 // default constructor
    MyCppClass testClass2(testClass1);     // copy constructor
    testClass1 = testClass2;               // copy assignment operator
 
    MyCppClass testClass3 = testClass1;    // copy constructor
 
    return 0;
}

执行结果:

这里需要注意的是,一般情况下我们总是以为在‘='运算符出现的地方都是调用copy assignment operator,上

面这种情况却是个例外。也就是,当一个新对象被定义的时候,即便这个时候是使用了'='运算符,它真实调用的是初始化函数copy constructor,而不是调用copy assignment operator去进行赋值操作。

Why初始化列表

一个对象在初始化时包括了两个步骤:

首先,分配内存以保存这个对象;

其次,执行构造函数。

在执行构造函数的时候,如果存在有初始化列表,则先执行初始化列表,之后再执行构造函数的函数体。那么,为什么会引入初始化列表呢?

C++与C相比,在程序组织上由“以函数为基本组成单位的面向过程”变迁到“基于以类为中心的面向对象”,与此同时类也作为一种复合数据类型,而初始化列表无非就是进行一些数据的初始化工作。考虑到这里,也可以较为自然的推测初始化列表与类这种数据类型的初始化有着关联。

在引入初始化列表之后,一个类对应数据成员的初始化就存在有两种方式。下面是类的数据成员类型分别为内置类型、自定义类型时的一个对比。 


// 数据成员类型为内置类型
class MyCppClass
{
public:
    // 赋值操作进行成员初始化
    MyCppClass 
    {
        counter = 0;
    }
    
    // 初始化列表进行成员初始化
    MyCppClass : counter(0)
    {
    }

private:
    int    counter;
}

当类的数据成员类型为内置类型时,上面两种初始化方式的效果一样。当数据成员的类型同样也为一个类时,初始化的过程就会有不一样的地方了,比如:


// 数据成员类型为自定义类型:一个类
class MyCppClass
{
public:
    // 赋值操作进行成员初始化
    MyCppClass(string name) 
    {
        counter = 0;
        theName = name;
    }

    // 初始化列表进行成员初始化
    MyCppClass : counter(0), theName(name)
    {
    }

private:
    int    counter;
    string theName;
}

在构造函数体内的theName = name这条语句,theName先会调用string的default constructor进行初始化,之后再调用copy assignment opertor进行拷贝赋值。而对于初始化列表来说,直接通过copy constructor进行初始化。

明显起见,可以通过如下的代码进行测试。


#include <iostream>
#include <string>
 
class SubClass
{
public:
    SubClass()
    {
        std::cout <<" In SubClass Default Constructor!" <<std::endl;
    }
 
    SubClass(const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Constructor!" <<std::endl;
    }
 
    SubClass& operator= (const SubClass& rhs)
    {
        std::cout <<" In SubClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
};
 
class BaseClass
{
public:
    BaseClass(const SubClass &rhs)
    {
        counter = 0;
        theBrother = rhs;
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const SubClass &rhs, int cnt):theBrother(rhs),counter(cnt)
    {
        std::cout <<" In BaseClass Default Constructor!" <<std::endl;
    }
 
    BaseClass(const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Constructor!" <<std::endl;
    }
 
    BaseClass& operator= (const BaseClass& rhs)
    {
        std::cout <<" In BaseClass Copy Assignment Operator!" <<std::endl;
 
        return *this;
    }
private:
    int counter;
    SubClass theBrother;
};
 
int main()
{
    SubClass subClass;
 
    std::cout <<"\nNo Member Initialization List: " <<std::endl;
    BaseClass BaseClass1(SubClass);
 
    std::cout <<"\nMember Initialization List: " <<std::endl;
    BaseClass BaseClass2(SubClass, 1);
 
    return 0;
}

执行结果:

也就是,在涉及到自定义类型初始化的时候,使用初始化列表来完成初始化在效率上会有着更佳的表现。这也是初始化列表的一大闪光点。即便对于内置类型,在一些情况下也是需要使用初始化列表来完成初始化工作的,比如const、references成员变量。这里有篇笔记,对初始化列表有着非常详尽的描述。

几个初始化名词

在阅读《Accelerated C++》中文版时,总是碰到“缺省初始化”、“隐式初始化”以及“数值初始化”,最初在理解这几个名词的时候几费周折,总觉得为什么一个初始化操作造出了如此多的名词,为此没少花时间来弄清楚它们之间的关系。

为了更好的理解它们,先对C++当中的数据类型进行简单划分。在C++里面,数据类型大致可以分为两种:第一种是内置类型,比如float, int, double等;第二种是自定义类型,也就是我们常用的class, struct定义的类。在对这些类型的数据进行初始化时,差别就体现出来了:对于内置类型,在使用之前必须进行显示的初始化,而对于自定义类型,初始化责任则落在了构造函数身上。


int x = 0;          // 显示初始化x
SubClass subClass;  // 依赖SubClass的default constructor进行初始化

上面的名词“缺省初始化”描述的就是当内置类型或者自定义类型的数据没有进行显示初始化时的一种初始化状态。而“隐式初始化”描述的是在该状态下面进行的具体操作方式,比如对于内置类型来说,缺省初始化状态下进行的隐式初始化实际上是未定义的,而自定义类型的隐式初始化则依赖于其constructor。

前面提到过C++不保证内置类型的初始化,但是当内置类型在作为一个类的成员时,在某些特定的条件下该内置类型的成员会被编译器主动进行初始化,对于这个过程也就是所谓的数值初始化。在《Accelerated C++》当中列出了如下的几种情况:

  1. 对象被用来初始化一个容器元素
  2. 为映射表添加一个新元素,对象是这个添加动作的副作用
  3. 定义一个特定长度的容器,对象为容器的元素

测试如下:


#include <iostream> 
#include <vector> 
#include <map> 
#include <string> 
 
using std::cout; 
using std::endl; 
using std::vector; 
using std::map; 
using std::string; 
 
class NumbericInitTestClass 
{ 
public: 
    void PrintCounter() 
    { 
        cout <<"counter = " <<counter <<endl; 
    } 
private: 
    int counter; 
}; 
 
 
int main() 
{ 
    NumbericInitTestClass tnc; 
    tnc.PrintCounter(); 
 
    map<string, int> mapTest; 
    cout <<mapTest["me"] <<endl; 
 
    vector<NumbericInitTestClass> vecNumbericTestClass(1); 
    vecNumbericTestClass[0].PrintCounter(); 
 
    return 0; 
}

对于没有进行初始化的内置类型,是一个未定义的值2009095316,而对于2, 3种情况来说,均被初始化为0,对于第1种情况我还没有想到合适的场景。

回过头想想,为了书中的一些相似的名词,去想办法把它们凑在一起总是显得有些牵强附会:)一些规则这里附上几条有关初始化的基本规则,它们多来源于《Effective C++》:

1. 为内置型对象进行手工初始化,因为C++不保证初始化它们。

2. 构造函数最好使用成员初值列(member initialization list),而不要在构造函数体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中声明的次序相同。

3. C++不喜欢析构函数吐出异常。

4. 在构造函数与析构函数期间不要调用virtual函数,因为这类调用从不下降至derived class。

5. copying函数应该确保复制“对象内所有成员变量”及“所有base class成分”。

参考文章

C++中对象初始化方式

c++类对象初始化方式总结

Peter87, C++中的对象初始化

到此这篇关于C++中的对象初始化的文章就介绍到这了,更多相关C++对象初始化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++中的对象初始化操作代码

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++中如何实现对象初始化操作代码

这篇文章给大家分享的是有关C++中如何实现对象初始化操作代码的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。当对象在创建时获得了一个特定的值,我们说这个对象被初始化。初始化不是赋值,初始化的含义是创建变量赋予其一个
2023-06-22

java对象初始化代码分享

这篇文章主要讲解了“java对象初始化代码分享”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java对象初始化代码分享”吧!一,实例变量的初始化这里首先介绍下创建对象的过程:类型为Dog的一
2023-05-30

C#中如何初始化对象

C#中如何初始化对象,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。具体示例如下:classPoint { publicintX{get;set;} publicint
2023-06-17

C#中怎么实现对象初始化

本篇文章给大家分享的是有关C#中怎么实现对象初始化,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。C#对象初始化器 (Object Initializers) :.NET2.0框
2023-06-17

C#3.0中对象初始化器和集合初始化器怎么理解

这篇文章主要讲解了“C#3.0中对象初始化器和集合初始化器怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#3.0中对象初始化器和集合初始化器怎么理解”吧!关于对象初始化器(Obje
2023-06-17

java中的对象怎么初始化

第一步:加载该类,一个java对象在初始化前会进行类加载,在JVM中生成Class对象。加载一个类会进行如下操作,下面给出递归描述。如果该类有父类,则先加载其父类。    i 初始化该类静态成员ii 执行该类静态代码块第二步:创建对象,如果该类有父类,则创建对
java中的对象怎么初始化
2015-01-14

Java中的对象是如何初始化的

对象初始化的过程:一:初始化类首先创建某个对象时:Dog dog = new Dog();首次访问某个类的静态方法或者静态字段时:Dog.staticFields;Java 解释器就会去找类的路径,定位已经编译好的 Dog.class 文件。二:获得类的资源然
Java中的对象是如何初始化的
2020-06-07

C++中单链表操作的示例代码

这篇文章主要为大家详细介绍了C++中单链表操作的示例代码,主要包括单链表的创建、元素的插入和删除等。文中的代码简洁易懂,需要的可以参考一下
2022-11-13

C++中顺序表操作的示例代码

这篇文章主要为大家详细介绍了C++中顺序表的基础操作的相关代码,主要有顺序表的输出、插入和删除数据等,感兴趣的小伙伴可以了解一下
2022-11-13

Android中实现自动生成布局View的初始化代码方法

在android开发过程中,界面布局是及其重要的,但同时也是复杂。有的时候我们急于实际运行查看布局效果。但是android的编译速度我实在不想吐槽啥,尤其在布局越来越复杂,项目越来越大,资源文件越来越多的情况下。 尤其是是android的v
2022-06-06

C++中怎么实现对象的拷贝与赋值操作

今天就跟大家聊聊有关C++中怎么实现对象的拷贝与赋值操作,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。拷贝构造函数,顾名思义,等于拷贝 + 构造。它肩负着创建新对象的任务,同时还要负
2023-06-17

C语言顺序表的基本操作(初始化,插入,删除,查询,扩容,打印,清空等)

这篇文章主要介绍了C语言顺序表的基本操作(初始化,插入,删除,查询,扩容,打印,清空等),具有很好的参考价值,希望对大家有所帮助。
2023-02-05

PHP7中新增的空合并运算符:如何简化代码的判空操作?

PHP7中新增的空合并运算符:如何简化代码的判空操作?在开发PHP应用程序时,经常会遇到需要判空的情况,例如获取用户输入的表单数据、从数据库查询结果中获取数据等等。以前的写法往往需要使用三元运算符或isset()函数来进行判断,代码看起来冗
2023-10-25

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录