cpp创建对象的多种形式

1 使用非默认构造函数来创建对象的几种形式

Person person = Person("binny1", 26);

这种方式创建对象,C++标准允许编译器使用两种方式来执行:

1、会创建一个临时对象,然后将临时对象复制到 person中,并丢弃临时对象。此时,将为临时对象调用析构函数,正如上面的结果。

编译器肯能立即删除临时对象,也可能过一会儿。

Person person2("binny2", 27);//会创建临时对象

这种格式更紧凑,它与显示调用等价。

Person *pPerson3 = new Person("binny3", 8);//不会创建临时对象

这条语句创建一个Person 对象,将其初始化为参数列表提供的值,并将该对象的地址赋给 pPerson指针。

这种情况下对象没有名字,但可以使用指针来管理对象。

如果编译器支持C++11,则可以用时列表初始化。只要提供的大括号列表中的数据与某个构造函数的参数列表匹配就可以。

Person person4 = {"binny4", 26};//c++11,会创建临时对象
Person person5{"binny5", 26};//c++11,会创建临时对象
Person *pPerson6 = new Person{"binny6", 26};//c++11,不会创建临时对象
Person person{};//与默认构造函数相匹配

2、使用默认构造函数来创建对象

Person person = Person();
Person person;

隐式调用默认构造函数,不要使用圆括号

头文件

c// // // Created by Biter on 2019/10/16.</p> <p>//</p> <h1>include</h1> <h1>ifndef NDK_PERSON_H</h1> <h1>define NDK_PERSON_H</h1> <p>using namespace std;</p> <p>class Person {</p> <p>private: char *mName; int mAge; public: Person();</p> <pre><code>Person(int age); Person(char *name, int age); void setName(char *name); void setAge(int age); char * getName(); int getAge(); ~Person(); </code></pre> <p>};</p> <h1>endif //NDK_PERSON_H</h1> <pre><code> ## 头文件的实现
//
// Created by Biter on 2019/10/16.

//

#include "../includes/Person.h"

Person::Person() {
mName = "biter";
mAge = 26;
}

Person::Person(char *name, int age) {
mName = name;
mAge = age;
cout << "Hello " << mName << endl;
}

Person::Person(int age) {
mAge = age;
}

void Person::setName(char *name) {
mName = name;
}

void Person::setAge(int age) {
mAge = age;
}

int Person::getAge() {
return mAge;
}

char *Person::getName() {
return mName;
}

Person::~Person() {
cout << "Bey " << mName << endl;
} ## 项目代码
//
// Created by Biter on 2019/10/15.

//
#include
#include "includes/Person.h"

int main() {

//第一种创建对象的方式
Person person1 = Person("binny1,显示调用构造函数", 26);
// //第二种创建对象的方式
Person person2("binny2,隐式调用构造函数", 27);
// //第三种创建对象的方式
Person *pPerson3 = new Person("binny3,动态创建对象", 8);

//c++11 列表初始化
Person person4 = {"binny4,显示列表初始化", 26};
Person person5{"binny5,隐式列表初始化", 26};
Person *pPerson6 = new Person{"binny6,动态列表初始化", 26};

std::cout << "person1 name = " << person1.getName() << std::endl;
std::cout << "person2 name = " << person2.getName() << std::endl;
std::cout << "person3 name = " << pPerson3->getName() << std::endl;
std::cout << "person4 name = " << person4.getName() << std::endl;
std::cout << "person5 name = " << person5.getName() << std::endl;
std::cout << "person6 name = " << pPerson6->getName() << std::endl;

Person person7 = 26;
person7.setName("p9");
std::cout << "p9.getAge() = " << person7.getAge() << std::endl;
return 0;
}

运行结果

C++提供了多种对象的创建方式,每次创建对象时(包括使用new动态分配内存),C++都是用类的构造函数。

从运行结果看出,动态创建对象的时候,没有调用析构函数。

小括号,老语法

Person person();//默认构造函数
Person person2("binny2", 27);//非默认构造函数
Person person1 = Person("binny1,显示调用构造函数", 26);
Person *pPerson3 = new Person("binny3,动态创建对象", 8);

大括号,新语法

Person person5{"binny5,隐式列表初始化", 26};
Person person4 = {"binny4,显示列表初始化", 26};
Person *pPerson6 = new Person{"binny6,动态列表初始化", 26};

无括号

如果构造参数只有一个参数,则可以将对象初始化为一个与该构造器参数类型相同的值,此时该构造将被调用。

Person person7 = 26;
std::cout << "p9.getAge() = " << person7.getAge() << std::endl;

由于没有初始化

,则析构函数被调用的时候输出乱码。修改如下:

Person person7 = 26;
p9.setName("p9");
std::cout << "p9.getAge() = " << person7.getAge() << std::endl;

C++构造函数初始化列表

构造函数的一项重要功能是对成员变量进行初始化,为了达到这个目的,可以在构造函数的函数体中对成员变量一一赋值,还可以采用 &#x521D;&#x59CB;&#x5316;&#x5217;&#x8868;

C++构造函数的初始化列表使得代码更加简洁。

#include
using namespace std;

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    Student(char *name, int age, float score);
    void show();
};

//采用初始化列表
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    //TODO:
}
void Student::show(){
    cout< show();

    return 0;
}

如本例所示,定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(当然也可以有其他语句),而是在函数首部与函数体之间添加了一个冒号 :,后面紧跟 m_name(name), m_age(age), m_score(score)语句,这个语句的意思相当于函数体内部的 m_name = name; m_age = age; m_score = score;语句,也是赋值的意思。

使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便,尤其是成员变量较多时,这种写法非常简单明了。

初始化列表可以用于全部成员变量,也可以只用于部分成员变量。下面的示例只对 m_name 使用初始化列表,其他成员变量还是一一赋值:

Student::Student(char *name, int age, float score): m_name(name){
    m_age = age;
    m_score = score;
}

注意,成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。请看代码:

#include
using namespace std;
class Demo{
private:
    int m_a;
    int m_b;
public:
    Demo(int b);
    void show();
};
Demo::Demo(int b): m_b(b), m_a(m_b){ }
void Demo::show(){ cout<
运行结果:
2130567168, 100

在初始化列表中,我们将 m_b 放在了 m_a 的前面,看起来是先给 m_b 赋值,再给 m_a 赋值,其实不然!成员变量的赋值顺序由它们在类中的声明顺序决定,在 Demo 类中,我们先声明的 m_a,再声明的 m_b,所以构造函数和下面的代码等价:

Demo::Demo(int b){
    m_a = m_b;
    m_b = b;
}

m_a 赋值时, m_b 还未被初始化,它的值是不确定的,所以输出的 m_a 的值是一个奇怪的数字;给 m_a 赋值完成后才给 m_b 赋值,此时 m_b 的值才是 100。

obj 在栈上分配内存,成员变量的初始值是不确定的。

初始化 const 成员变量

构造函数初始化列表还有一个很重要的作用,那就是初始化 const 成员变量。初始化 const 成员变量的唯一方法就是使用初始化列表。例如 VS/VC 不支持变长数组(数组长度不能是变量),我们自己定义了一个 VLA 类,用于模拟变长数组,请看下面的代码:

class VLA{
private:
    const int m_len;
    int *m_arr;
public:
    VLA(int len);
};
//必须使用初始化列表来初始化 m_len
VLA::VLA(int len): m_len(len){
    m_arr = new int[len];
}

VLA 类包含了两个成员变量, m_lenm_arr 指针,需要注意的是 m_len 加了 const 修饰,只能使用初始化列表的方式赋值,如果写作下面的形式是错误的:

class VLA{
private:
    const int m_len;
    int *m_arr;
public:
    VLA(int len);
};
VLA::VLA(int len){
    m_len = len;
    m_arr = new int[len];
}

Original: https://www.cnblogs.com/burner/p/cpp-chuang-jian-dui-xiang-de-duo-zhong-xing-shi.html
Author: 浪客禅心
Title: cpp创建对象的多种形式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/606502/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用

    本文简要说一下ASP.NET Core 在Docker中部署以及docker-compose的使用 (ASP.NET Core 系列目录)。 系统环境为CentOS 8 。 一、概…

    Linux 2023年6月7日
    0106
  • 【开源打印组件】vue-plugin-hiprint初体验

    vue-plugin-hiprint的学习与应用 😄 生命不息,写作不止🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 一个有梦有戏的人 @怒放吧德德🌝分享学…

    Linux 2023年6月6日
    0173
  • 记一次因网络变更导致zabbix连接es报400和404

    背景 zabbix历史数据存储到es集群,正常工作中的时候,因网络变更导致zabbix server连接不上es,zabbix日志首先报400错误,之后一直404,es那边报查询相…

    Linux 2023年6月7日
    0106
  • 不同网段之间实现GDB远程调试功能

    在开发过程中,使用gdb远程调试时,会碰到 Linux 服务器的网段和板载设备的网段不是一样的,不能正常使用 gbd 远程调试功能。 板载设备和电脑连接路由器,属于同一个网段,如1…

    Linux 2023年6月7日
    097
  • redis压力测试【转】

    本文转自: https://segmentfault.com/a/1190000015571891 redis自带的redis-benchmark工具 Redis 自带了一个叫re…

    Linux 2023年5月28日
    072
  • Linux服务器配置git服务

    现在 Github 已经支持个人建立私有仓库,包括国内的一些开源平台如 Gitee 等也支持私有仓库,所以直接去建立私有仓库即可。或者可以自己买服务器搭建 GitLab。但是这篇文…

    Linux 2023年6月14日
    0109
  • Tomcat启动乱码

    1、找到安装的tomcat的conf目录2、找到logging.properties配置文件3、在文件中找到 java.util.logging.ConsoleHandler.en…

    Linux 2023年6月7日
    0106
  • redis cluster 数据迁移

    1,先停止java的后台和.net的后台,停止对redis cluster进行访问 2,然后 cd /usr/local/redis-cluster/7001 每个节点都要做如下操…

    Linux 2023年5月28日
    088
  • 用 shell 脚本制造连接频繁中断的场景

    问题的提出 最近在准备客户端的新版本,在内部灰度过程中,发现一类奇怪的 dump,通过查看日志和堆栈,可以确定是因为每次连上后台就被后台断开了、导致多次重连后随机发生的崩溃。dum…

    Linux 2023年6月6日
    092
  • 运维故障收集-考勤机无法连接考勤机网关系统故障验证流程

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/linuxshare/p/16474967.htmlAu…

    Linux 2023年6月6日
    097
  • DHCP超级作用域

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    087
  • Pycharm设置python文件头

    设置路径为: File->Setting->Editor->File and code Templates->Python Script 可用的预定义文件模…

    Linux 2023年6月8日
    0111
  • 线段树扫描线(一) 矩形面积 以hdu 1542为例

    还是老规矩,传送门 hdu 1542 不做过多解释了,就是给出n个矩形,求出这些矩形所覆盖的面积和。由于n很小,因而这道题不是必须用线段树 先想想怎么办,先来一个例图(稍微有点复杂…

    Linux 2023年6月6日
    0109
  • 用华为云cli(命令行程序),管理华为云服务器的,安全组端口

    关键字 hcloud 华为 命令行 linux windows powershell 前些天,大家因为华为云,是否应该默认开启端口,大家吵起来了,所以我抽空写了此文。解决问题,缓解…

    Linux 2023年6月14日
    089
  • 网络设备配置–6、通过RIP协议配置动态路由

    一、前言 同系列前几篇:网络设备配置–1、配置交换机enable、console、telnet密码网络设备配置–2、通过交换机划分vlan网络设备配置&#8…

    Linux 2023年6月8日
    0106
  • 方法的深度理解

    权限修饰符 返回值类型 类名(行参列表 )throws 异常的类型{ //方法体 约定俗称:子类中叫重写的方法,父类中叫被重写的方法。 ①子类重写的方法名和行参列表和父类被重写的方…

    Linux 2023年6月14日
    0100
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球