- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章c++基础语法:构造函数与析构函数由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
一.构造函数 。
类似于java,C++中也有构造函数的概念,相关用法如下:
1.1 构造函数的定义 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#include <iostream>
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;
}
//定义普通成员函数
void
Student::show(){
cout<<m_name<<
"的年龄是"
<<m_age<<
",成绩是"
<<m_score<<endl;
}
int
main(){
//创建对象时向构造函数传参
Student stu(
"小明"
, 15, 92.5f);
stu.show();
//创建对象时向构造函数传参
Student *pstu =
new
Student(
"李华"
, 16, 96);
pstu -> show();
return
0;
}
|
运行结果:
小明的年龄是15,成绩是92.5 李华的年龄是16,成绩是96 。
1.2 构造函数的重载 。
构造函数同样也支持重载操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#include <iostream>
using
namespace
std;
class
Student{
private
:
char
*m_name;
int
m_age;
float
m_score;
public
:
Student();
Student(
char
*name,
int
age,
float
score);
void
setname(
char
*name);
void
setage(
int
age);
void
setscore(
float
score);
void
show();
};
Student::Student(){
m_name = NULL;
m_age = 0;
m_score = 0.0;
}
Student::Student(
char
*name,
int
age,
float
score){
m_name = name;
m_age = age;
m_score = score;
}
void
Student::setname(
char
*name){
m_name = name;
}
void
Student::setage(
int
age){
m_age = age;
}
void
Student::setscore(
float
score){
m_score = score;
}
void
Student::show(){
if
(m_name == NULL || m_age <= 0){
cout<<
"成员变量还未初始化"
<<endl;
}
else
{
cout<<m_name<<
"的年龄是"
<<m_age<<
",成绩是"
<<m_score<<endl;
}
}
int
main(){
//调用构造函数 Student(char *, int, float)
Student stu(
"小明"
, 15, 92.5f);
stu.show();
//调用构造函数 Student()
Student *pstu =
new
Student();
pstu -> show();
pstu -> setname(
"李华"
);
pstu -> setage(16);
pstu -> setscore(96);
pstu -> show();
return
0;
}
|
运行结果:
小明的年龄是15,成绩是92.5 成员变量还未初始化 李华的年龄是16,成绩是96 。
1.3 默认构造函数 。
类似于java,如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个构造函数的函数体是空的.
注意:调用没有参数的构造函数也可以省略括号.
Student *stu = new Student,
Student *stu = new Student(),
以上两种写法是等价的.
1.4 构造函数的参数初始化表 。
构造函数的主要目的是用于对成员变量进行初始化, 为了达到这个目的,可以在构造函数的函数体中对成员变量一一赋值,还可以采用参数初始化表。具体写法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <iostream>
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<<m_name<<
"的年龄是"
<<m_age<<
",成绩是"
<<m_score<<endl;
}
int
main(){
Student stu(
"小明"
, 15, 92.5f);
stu.show();
Student *pstu =
new
Student(
"李华"
, 16, 96);
pstu -> show();
return
0;
}
|
运行结果:
小明的年龄是15,成绩是92.5 李华的年龄是16,成绩是96 。
1.5 使用参数初始化表来初始化const成员变量 。
参数初始化表还有一个很重要的作用,那就是初始化 const 成员变量。初始化 const 成员变量的唯一方法就是使用参数初始化表。例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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];
}
|
二.析构函数 。
创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是析构函数.
析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号.
注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,编译器会自动生成一个默认的析构函数.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
VLA {
public
:
VLA(
int
len);
~VLA();
// 析构函数
private
:
const
int
m_len;
int
*m_arr;
};
VLA::VLA(
int
len):m_len(len) {
// 构造函数初始化
if
(len > 0) {m_arrz =
new
int
[len];};
else
{m_arr = NULL;};
}
VLA::~VLA() {
delete
[]m_arr;
// 在析构函数中释放堆区申请的内存
}
|
C++ 中的 new 和 delete 分别用来分配和释放内存,它们与C语言中 malloc()、free() 最大的一个不同之处在于:用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。构造函数和析构函数对于类来说是不可或缺的,所以在C++中我们非常鼓励使用 new 和 delete.
下面是其他网友的补充 。
说实话c++还是以前在学校的时候用过的,从毕业到现在一直用c嵌入式编程,现在重新搬出C++语法 ,如果理解上有错误的地方,还请路过的朋友多指正~~~ 。
构造函数用来构造一个对象,主要完成一些初始化工作,如果类中不提供构造函数,编译器会默认的提供一个默认构造函数(参数为空的构造函数就是默认构造函数) ;析构函数是隐式调用的,delete对象时候会自动调用完成对象的清理工作.
现在主要看看继承中的构造函数和析构函数的调用:
1
2
3
4
5
6
7
8
|
class
A {} ;
class
B :
public
A
{};
class
C :
public
B
{};
c * ptr =
new
C() ;
delete
ptr ;
|
一般来说,上面的代码构造函数是先调用最根父类的构造函数,然后是次一级父类构造函数,依次而来直到派生类本身的构造函数,而且对父类构造函数的调用都是父类的默认构造函数(当然也可以显示地调用父类的非默认构造函数),也就是说派生类在构造本身之前会首先把继承来的父类成分先构造好; 对析构函数的调用是先调用派生类本身的析构函数,然后是上一层父类析构函数,直到根父类析构函数 ,当没有多态的时候,析构函数是这样调用的.
改一下上面的代码:
A * ptr = new C() ; delete ptr ,
在多态的情况下,如果基类A中的析构函数不是虚构造函数,则当delete ptr的时候只会调用A的析构函数,不会调用B和C中的析构函数;如果A中的析构函数是虚构造函数就会调用所有的析构函数,调用顺序和一般情况一样.
再改一下上面的代码:
B *prt = new C(); delete ptr ,
在多态的情况下,如果A,B中的析构函数都不是虚析构函数,则当delete ptr的时候先调用B的析构函数,再调A的析构函数,不会调用C中的析构函数,如果A或者B中至少有一个是虚析构函数,则析构函数调用和一般情况一样.
因此总结一下规律:
CA * ptr = new CB() ; delete ptr ,
CB是CA的子类,构造函数的调用一直是一样的,当具备多态的时候: 如果CA及其父类都不具备虚析构函数,则首先调用A的析构函数,然后调用A的父类析构函数直到根父类析构函数,不会调用A以下直到派生类的析构函数 ;如果如果CA及其父类只要有一个具备虚析构函数,则析构函数调用跟一般情况一样.
因此:带有多态性质的基类应该声明虚析构函数 ,这样的基类一般还有其他虚函数; 如果类的设计不是用于基类,而且不具备多态性,则析构函数不应该声明为虚析构函数 。
小测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#include<iostream>
using
namespace
std ;
class
A
{
public
:
A(){cout<<
"A constructor"
<<endl;}
A(
char
* arp) { cout <<
"not default "
;}
~CA(){cout<<
"A desstructor"
<<endl;}
};
class
B:
public
A
{
public
:
B(){cout<<
"B constructor"
<<endl;}
~B(){cout<<
"B desstructor"
<<endl;}
};
class
C:
public
B
{
public
:
C(
char
* arp){cout<<
"C constructor"
<<endl;}
~C(){cout<<
"C desstructor"
<<endl;}
};
int
main()
{
C * ptr =
new
C(
"hello world"
) ;
delete
ptr ;
}
|
另外effective C++中提到的:
1、析构函数不能吐出异常,如果析构函数掉用的函数可能产生异常,要在析构函数内部进行捕获进行处理,因为如果析构函数抛出异常的话,比如说vector,当调用各个对象的析构函数进行删除的时候可能导致抛出多个异常,从而使程序进入不确定状态.
2、如果用户需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数执行这个操作.
3、在构造函数和析构函数中都不应该调用虚函数,这是因为当调用构造函数构造对象的时候,首先会调用父类的构造函数,此时对象的类型在编译器看来就是一个父类对象(实际此时子类成员还处于不确定状态),会调用父类的虚函数,而不会调用子类的虚函数.
最后此篇关于c++基础语法:构造函数与析构函数的文章就讲到这里了,如果你想了解更多关于c++基础语法:构造函数与析构函数的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
谁能解释一下原因: (define a (lambda() (cons a #f))) (car (a)) ==> procedure ((car (a))) ==> (procedure . #f)
这是 PyBrain 网站的摘录。我了解大部分正在发生的事情,但是一行让我完全难住了。我以前从未在 python 代码中看到过这样的东西。这是整个循环,对于上下文: for c in [0,
我是gradle / groovy的新手。我想创建将做一些事情的自定义任务。我的第一个问题是任务完成时该如何做?我可以覆盖doFirst / doLast闭包吗?也许我可以重写某些在开始和结束时都会执
我刚刚开始评估 MS 企业库。他们使用以下指令来获取实例: var customerDb = EnterpriseLibraryContainer.Current.GetInstance("C
这是我的 if else Ansible 逻辑.. - name: Check certs exist stat: path=/etc/letsencrypt/live/{{ rootDomain
我正在使用construct 2.8 对一些失传已久的 Pascal 程序创建的一些文件的 header 进行逆向工程。 header 由许多不同的记录组成,其中一些是可选的,我不确定顺序是否固定。
我在将 getchar() 的输入放入 char *arr[] 数组时遇到问题。我这样做的原因是因为输入数据(将是一个带有命令行参数的文件)将存储在一个 char 指针数组中以传递给 execvp 函
通常我们不能约束类型参数 T派生自密封类型(例如 struct 类型)。这将毫无意义,因为只有一种类型适合,因此不需要泛型。所以约束如下: where T : string 或: where T :
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 9 年前。 Improve th
#include using namespace std; class A { private: int m_i; friend int main(int argc, char cons
这个问题在这里已经有了答案: Are there legitimate uses for JavaScript's "with" statement? (33 个答案) 关闭 9 年前。 我有这个代
在this answer我看到了下一个 Bash 结构。 yes "$(< file.txt)" 什么意思 "$(< file.txt)" ? 我明白了 命令替换 - $(command)用命令的结
if (a == 1) //do something else if (a == 2) //do something else if (a == 3) //do somethi
关于构造的快速简单的问题。 我有以下用于将项目添加到 ListView 的代码。 ListViewItem item = new ListViewItem(); item.Text = file; i
我想使用 std::vector 来控制给定的内存。首先,我很确定这不是好的做法,但好奇心占了上风,无论如何我都想知道如何做到这一点。 我遇到的问题是这样的方法: vector getRow(unsi
下面显示了一段简单的javascript: var mystring = ("random","ignored","text","h") + ("ello world") 这个字符串会生成 hello
在 Java 中,创建对象的标准方法是使用 MyClass name = new MyClass(); 我也经常看到构造 new MyClass() { /*stuff goes in here*/
我正在编写 C++ ndarray 类。我需要动态大小和编译时大小已知的数组(分别分配自由存储和分配堆栈)。我想支持从嵌套的 std::initializer_list 进行初始化。 动态大小的没问题
我正在将一个项目从 Visual Studio 2005 转换为 Visual Studio 2008,并提出了上述结构。 using Castle.Core.Resource; using Cast
我想知道我在这里的想法是否正确,我主要针对接口(interface)进行编程,所以我想知道下面的类是否应该通过 DI 注入(inject),或者我应该自己实例化一个类... 注意:这些服务保存在我的核
我是一名优秀的程序员,十分优秀!