网站首页 > 基础教程 正文
为啥今天要聊这个话题了,主要是复杂系统内部C++没有自动空间回收机制,用的不好会出现一堆问题~
1. 定义
C++中的构造函数可以分为5类:默认构造函数、普通构造函数、拷贝构造函数、转换构造函数、移动构造函数
1.1. 默认构造函数
也称为合成默认构造函数,隐式声明(用户没有声明、编译器声明)的构造函数。如果定义某个类的变量(对象)时没有提供初始化式就会使用默认构造函数。
class Student
{
int age;
string name;
Student() {} //如果定义了这个构造函数,11行编译报错
}
int main()
{
Student s = {1, 1, 1.1};
}
当一个类没有构造函数时,如果满足以下四个条件其中之一,则编译器会为该类自动生成一个默认的构造函数:
- 该类含有一个类类型(非内置类型)的成员变量,且该类型含有默认构造函数。
- 该类继承自含有默认构造函数的基类。
- 该类继承或声明了虚函数。
- 该类含有虚基类。
合成的默认构造函数只适合非常简单的类,有三个原因:
- 只有当类没有声明任何构造函数的时侯,编译器才会自动地生成默认构造函数。
- 对于某些类来说,合成的默认构造函数可能执行错误的操作。如果类包含有内置类型或者复合类型的成员,则只有当这些成员全都被赋予了类内的初始值时,这个类才适合于使用合成的默认构造函数。
- 有的时候编译器不能为某些类合成默认的构造函数。例如:类中包含一个其他类类型的成员且这个成员的类型没有默认构造函数,那么编译器将无法初始化该成员。这种时候,我们必须自定义默认构造函数,否则该类将没有任何可用的默认构造函数。我们可以使用 = default 要求编译器生成合成的版本(可以对默认构造函数和拷贝控制成员使用)。在新标准下,我们可以定义删除的函数(deleted function)——使用 = delete 。删除的函数是这样一种函数:我们虽然声明了它们,但不能以任何方式来使用它们。
问题:是不是所有的类都有默认构造函数?答案是否定的。
虽然编译器会为类自动生成一个合成的默认构造函数,但是它仅对于包含类类型成员的类。所以对于只含有内置类型或复合类型成员的类,如果不自定义他们的构造函数,编译器不会自动为其生成一个合成的默认构造函数的!
1.2. 普通构造函数
C++用于构建类的新对象时需要调用的函数
Student(int age,string name); //有参数
1.3. 拷贝构造函数
当一个类没有拷贝构造函数时,如果满足以下四个条件其中之一,则编译器会为该类自动生成一个默认的拷贝构造函数:
- 该类含有一个类类型(非内置类型)的成员变量,且该类型含有拷贝构造函数。
- 该类继承自含有拷贝构造函数的基类。
- 该类继承或声明了虚函数。
- 该类含有虚基类。
需要注意的是,默认的拷贝构造函数实现的是浅拷贝。
Student(Student& stu)
1.4. 转换构造函数
一个构造函数接收一个不同于其类类型的形参,可以视为将其形参转换成类的一个对象。像这样的构造函数称为转换构造函数。在 C++ string 类中可以找到使用转换构造函数的实用示例。string 类提供一个将 C 字符串转换为 string 的转换构造函数
class string
{
//仅显示转换构造函数
public:
string(char *);//形参是其他类型变量,且只有一个形参
};
1.5. 移动构造函数(C++11)
所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象。简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”。事实上,对于程序执行过程中产生的临时对象,往往只用于传递数据(没有其它的用处),并且会很快会被销毁。因此在使用临时对象初始化新对象时,我们可以将其包含的指针成员指向的内存资源直接移给新对象所有,无需再新拷贝一份,这大大提高了初始化的执行效率。
Student(Student&& stu)
2. 空类
一个类默认会生成哪些函数
class Empty{ };
默认会生成以下几个函数
- 无参的构造函数
在定义类的对象的时候,完成对象的初始化工作。
Empty(){}
- 拷贝构造函数
拷贝构造函数用于复制本类的对象
Empty(const Empty& copy){}
- 赋值运算符
Empty& operator = (const Empty& copy){}
- 析构函数(非虚):C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
~Empty(){}
3. 场景
3.1. 浅拷贝下的内存重复释放
class Test
{
public:
int *p;
Test() { p = new int; };
~Test() { delete p; };
};
void main()
{
Test t1;
Test t2(t1);
Test t3 = t1;
}
t1、t2、t3的成员变量p指向的是同一块内存,程序结束后会出现重复释放的问题。
为了解决这个问题,可以有如下三种方法:
自定义拷贝构造函数
class Test
{
public:
int *p;
Test(const Test &t)
{
p = new int (*(t.p));
}
Test() { p=new int; };
~Test() { delete p; };
};
声明该类为不可拷贝和赋值的类
class NonCopyable {
protected:
NonCopyable() {}
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
class Test:public NonCopyable
{
public:
int *p;
Test() { p=new int; };
~Test() { delete p; };
};
使用C++11的【右值引用】新特性来解决
Student && stu2 = std::move( stu );
3.2. 深拷贝的性能问题
class Test
{
public:
int *p;
Test(const Test &t)
{
p = new int (*(t.p)); cout << "copy construct" << endl;
}
Test() { p=new int; cout << "construct" << endl; };
~Test() { delete p; cout << "destruct" << endl; };
};
Test getTest()
{
return Test();
}
void main()
{
{
Test t = getTest();
}
}
g++ -o copy copy.cpp -fno-elide-constructors
输出:
construct
copy construct
destruct
copy construct
destruct
destruct
可以看到,进行了两次的深拷贝,对于对内存要求不高、本例这种占内存比较小的类Test而言(申请的堆空间小),可以接受。
但如果临时对象中的指针成员申请了大量的堆空间,那将严重影响程序的执行效率。
C++11为了解决这一问题(深拷贝占用大量空间),引入移动构造函数。
class Test
{
public:
int *p;
Test(Test &&t) //移动构造函数
{
p = t.p;
t.p = nullptr;//将临时对象的指针赋值为空
cout << "move construct" << endl;
}
Test(const Test &t) //拷贝构造函数
{
p = new int (*(t.p));
cout << "copy construct"<< endl;
}
Test() { p=new int; cout << "construct" << endl; };
~Test() { delete p; cout << "disconstruct" << endl; };
};
Test getTest()
{
return Test();
}
void main()
{
{
Test t = getTest();
}
}
输出:
construct
move construct
disconstruct
move construct
disconstruct
disconstruct
猜你喜欢
- 2024-10-12 全面剖析 C++ Boost 智能指针!| CSDN 博文精选
- 2024-10-12 C++设计模式——原型模式 设计模式之原型模式
- 2024-10-12 如何攻克 C++ 中复杂的类型转换? c++中四种类型转换的方式
- 2024-10-12 C++|由成员函数到运算符重载(类内、类外、友元方式重载)
- 2024-10-12 C++11新特性(49)- 用移动类对象代替拷贝类对象
- 2024-10-12 C++类的默认成员函数 c++类中定义的成员默认访问属性为( )
- 2024-10-12 C++的23种设计模式(上篇-创建型模式)
- 2024-10-12 C++构造函数和析构函数详解 c语言构造函数和析构函数
- 2024-10-12 c++——默认成员函数 c++成员变量默认值
- 2024-10-12 C++|类中实现操作符重载,用操作符代替成员函数名
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)