cstring转const char*函数_Chapter3:构造函数和析构函数(一)

a62b3fd5f4652853513c425ef5b05470.png

C++在C的基础上引入了继承、多态和重载的特性,它们很大程度上建立在类的基础上。类的结构:成员变量和成员函数,每本C++的教材上都会写,每个初学C++的人也一定能掌握。但是只了解这些,学习到的不能称为C++,应该叫做C with class:初学者只把class当作是一种复杂的、能绑定函数的数据结构在使用。所以今天先从类的起点和终点——建立和销毁说起。

C++中有一类特殊的成员函数,名字和类名相同(析构函数前面加上~),无返回值,也无需用户显式调用,创建和销毁对象时自动执行,这种特殊的成员函数就是构造函数(Constructor)和析构函数( Destructor )。

1.从最简单的构造函数开始

#include

此class仅供参考,使用了两个对象,其中sam在栈上,jack在堆上。为了安全使用字符指针,在析构函数中使用new开辟空间,然而这是分配在堆上的空间,需要使用完之后在其他地方安全释放,故在析构函数里面使用这个操作,当然new和delete相关会在后续的内存分配文章中介绍。如果把类中构造函数和析构函数的声明、类外构造函数和析构函数的定义注释掉,编译器报告下列错误:

e93e2692bdc0e2806bd881912af497c6.png

原因是因为笔者传入的是字符串常量,没有析构函数,编译器无法自动完成类型转换,存入数据,更无从谈起下一步的释放空间了。

通常情况下,不涉及类型转换、空间分配和释放、指针操作等复杂功能的构造函数和析构函数,是可以不写出,由编译器直接提供默认的People(){},没有参数也不执行任何动作;它们必须是public属性,否则在外部无法调用;没有返回值——它隐藏的返回值是this指针。

这里补充一下为什么不可以有具体的返回值:

c++构造函数有返回值吗?​www.zhihu.com
097fb1fd76afffb93afe8f5ceb74bcd2.png

3425e443658dc11a705a81b8a329357b.png

从实际上说,此处的ECX就是指针this,实际上对象在调用构造函数之前就存在。从语义上说,举一个建立在这个错误上的例子:

class A{
public:
    int A():x(0){return 1;}
    A(int i):A(i){}
private:
   int x;
}

假设我们这样使用第一个构造函数,那么它返回1给临时对象,对象接收到返回值,认为是一个参数,使用第二个构造函数,这样以来就不知道创建的对象究竟是A(0)还是A(1)了,产生了二义性。

构造函数的调用是强制性的,只要在类中定义了构造函数,创建对象时一定调用。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中一个构造函数来匹配,也即创建对象是只有一个构造函数会被调用。对于文章最前面提出的例子,如果使用People sam()或者new sam()(无参数不加括号也可以)就是错误的,没有无参数的构造函数。用户自定义构造函数之后,编译器都不再自动生成。

如果构造函数的内容比较简单,可以采用初始化列表的办法:

#include<iostream>
#include<cstring>

class People{
private:
   const char* name;
   int age;
public:
   People(const char* name, int age);
   ~People();
   void SetAge(int age);
   int GetAge();
};

People::People(const char *name, int age):name(name), age(age){};

People::~People(){
    this->name = nullptr;
}

void People::SetAge(int age){
    this->age = age;
}

int People::GetAge(){
    return age;
}

int main(){
    const char *p = "sam";
    People sam(p, 20);
    sam.SetAge(15);
    std::cout<<sam.GetAge()<<std::endl;
    
    const char *q = "jack";
    People *jack = new People(q, 16);
    jack->SetAge(16);
    std::cout<<jack->GetAge()<<std::endl;
    return 0;
}

在这个例子中,初始化了const成员变量。为什么const成员变量只能在初始化列表中使用呢?一、常量只能初始化不能赋值,函数体内是赋值,若我们将const和引用类型的成员变量放在构造函数的函数体内的话。那么const和引用类型的变量将会没有初值,即没被初始化;二、构造函数不能被声明为const函数,因此当我们创建一个类的const对象时,直到构造函数完成初始化的过程,对象才真正取得其“常量”的属性,因此,构造函数在const对象的构造过程中可以向其写值;三、主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。

#include<iostream>
#include<cstring>

class People{
private:
   const char* name;
   int p_age;
   int p_id;
public:
   People(const char* name, int age, int id);
   ~People();
   void SetAge(int age);
   int GetAge();
};

People::People(const char *name, int age, int id):name(name), p_id(id), p_age(p_id){}

People::~People(){
    this->name = nullptr;
}

void People::SetAge(int age){
    this->p_age = age;
}

int People::GetAge(){
    return p_age;
}

int main(){
    const char *p = "sam";
    People sam(p, 20, 14);
    std::cout<<sam.GetAge()<<std::endl;
    
    const char *q = "jack";
    People *jack = new People(q, 16, 13);
    std::cout<<jack->GetAge()<<std::endl;
    return 0;
}

需要特别注意的是,成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关,把上面的例子修改成这样:

People::People(const char *name, int age, int id):name(name), p_id(id), p_age(p_id){}

输出的结果是两个0,显然是错误的。因为实际上,它等效于这样。在还变量还未初始化的时候就使用。

People::People(const char *name, int age, int id){
    this->name = name;
    this->p_age = this->p_id;
    this->p_id = id;
}

到此为止介绍了简单的构造函数特性,这个部分很长,估计会花好几篇写完。

(如有转载请注明作者与出处,欢迎建议和讨论,thanks)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值