内容提要:类和对象
1 默认构造函数(不带参数的构造函数)
1)如果一个类没有显示定义任何构造函数,那么编译器会默认提供一个空的构造器实现;
2)如果一个类显示定义了一个带参数的构造函数,那么编译器不再提供默认的构造器函数;
此时如果需要默认构造器,需要显示定义
class People {
private:
std::string mName;
public:
void setName(const std::string& name);
const std::string& getName() const;
}
void People::setName(const std::string& name) {
this->mName = name;
}
const std::string& getName() const {
return this->mName;
}
以上代码,会有一个空实现的默认构造函数。
People a;//此时会在堆栈上自动调用默认构造函数生成一个People对象
People* p = new People();//此时会在堆上使用默认构造函数生成一个People对象
2 堆栈/堆上的对象创建方式
从上面的代码可以看到堆栈上/堆上使用默认构造函数创建对象的方式。
如果没有默认构造函数,而是People::People(const std;;string& name)这样的构造函数,那么创建对象的方式就变成:
People a("myname);//只能这样创建
People* p = new People("myName");//
3 对象的释放
1)堆栈中的对象创建以后,可以自动释放,不需要干预 ;
2)堆中通过new的方式创建对象以后,需要使用delete的方式销毁对象,同时将对应指针设置为nullptr
或者使用智能指针创建对象(可以不用干预对象的释放)
People a("name");//自动释放
People* p = new People("name");//
//other code
delete p;//销毁对象
p = nullptr;//避免后面错误使用野指针
auto people = make_unique<People>("name");//智能指针,自动释放
//或者如下等价方式
unique_ptr<People> pp(new People("name"));
4 重载的概念
C++中,如果一个类的多个函数具有相同的名字,但是形参不一样(参数类型不一样或者参数顺序不一样)
5 构造函数中调用构造函数?
如果在构造函数中调用其他的构造函数,只是创建了一个临时未命名对象。不会初始化对象。
6 显示控制是否有默认构造函数
class A {
public:
A() = default;//生成默认构造函数
A(int a);
private:
int a;
} ;
class B {
public:
B() = delete;//不生成默认构造函数,如果这个类只想包含静态成员变量和静态成员方法会很有用
}
7 构造函数初始化器
一般我们初始化成员变量,都是在构造函数函数体内进行初始化,比如:
A::A() {
a = 0;
}
C++还提供了一种在构造函数中初始化成员变量的方法,叫做构造函数初始化器(ctor-initializer)。语法如下:
A::A() : a(0)
{
}
构造函数初始化器出现在构造函数参数列表和构造函数体的左花括号之间。这个列表以冒号开始,使用逗号分隔开。
构造函数初始化器和在构造函数体中初始化成员变量的区别在于:
其实以上是2个步骤:
1)先为对象创建成员变量;->创建成员变量
2)然后调用构造函数为成员变量赋值;->改变成员变量的值
构造函数初始化器的目的是在第1)步在创建成员变量的时候,就给这些成员变量自己的构造函数传递初始值来创建成员变量。
构造函数中初始化则是在第2)步更改已经创建好的成员变量的值;
从以上分析可以看出:构造函数初始化器可以避免冗余操作,更加高效。
同时考虑这样的场景:如果一个类A 包含一个没有无参构造函数的成员变量B,那么想要构造A,就必须使用ctor-initializer
class B {
public:
B(int b);
};
class A {
public:
A();
private;
B b;
}
A::A() {
}
A* a = new A();
以上代码将编译出错,分析如下:
1)new A()的时候,将会调用A的构造函数。
2)调用A的构造函数时,会先创建成员变量b,此时隐含了如下操作: B b; 会调用 B的默认构造函数。但是此时找不到B的默认构造函数,此时就会报错。
解决方案:
A::A(): b(1) {
}
此时在调用A的构造函数的时候,先尝试去构造b,此时调用B的构造函数B(int i),构造成功。不会出错。
结论:
只要一个类包含没有无参构造函数的成员变量,就必须使用ctor-initializer初始化这些成员变量。
注意:
ctor-initializer初始化成员变量的顺序必须严格按照在类中定义时出现的顺序,而不是出现在ctor-initializer列表中的顺序。
例如:
class A {
private:
double mValue;
std::string mString;
}
A::A(const string& s): mString(s),mValue(stringToDouble(mString))
{
}
以上代码的实际初始化顺序为:
- 先看mValue(stringToDouble(mString)),此时发现mString还没有初始化,所以为null,所以实际上是mValue(stringToDouble(null)),然后 mValue获得一个初始值;
- mString(s),此时用s初始化mString
=> 所以以上顺序并不是按照ctor-initializer列表的顺序初始化成员变量!!!
备注:有几个场景必须要使用构造函数初始化器来初始化:
1)const数据成员
由于const数据成员在创建以后无法再对其赋值。必须在创建的时候提供值。
2)引用数据成员
引用数据成员必须提供一个值才可以存在。所以必须在构造函数初始化器中初始化。
3)没有默认构造函数的对象数据成员
C++使用默认构造函数初始化成员对象,如果不提供默认构造函数,就无法初始化这个对象。
4)没有默认构造函数的超类