大家好,今天再次写c 的文章,首先给各位网友说明一下这段时间为啥都是写c 的文章,没有Linux的文章;原因是这样的,自己立了一个flag,八月份把c 的基本语法全部过完(目前完成三分之一),所以文章过程中我写的可能没有很详细的解释一些概念;所以文章中有不理解的地方,可以私聊我,我会尽力解答好。同时昨天遇到一位刚高考完的网友,人家都这么努力学习,你还打酱油吗?
一、对象的析构顺序:
在上一篇文章里面,已经介绍过对象的构造顺序,既然有对象的产生,那么就会有对象的消亡;换句话说,有资源的分配,最终系统就要收回这些分配出去的资源。所以也就有了对象的析构顺序用法了。下面我们来看一下对象析构顺序的分类,不过这里我们先来看构造对象调用构造函数的来引出对象析构的顺序:
(1)单个对象创建时构造函数的调用顺序:
- 调用父类的构造过程(这个概念暂时还没学,先不用管)
- 调用成员变量的构造函数(调用顺序与声明顺序相同)
- 调用类自身的构造函数
引出:析构函数与对应的构造函数的调用顺序相反,也就是对象构造与对象析构(消亡)的顺序相反。
(2)代码演示:
代码语言:javascript复制#include <stdio.h>
class Test
{
const char * i;
public:
Test(const char *s)
{
printf("Test(const char *s) is %sn",s);
i=s;
}
~Test()
{
printf("~Test() is %sn",i);
}
};
class Test2
{
Test mA;
Test mB;
public:
Test2():mB("mB"),mA("mA")
{
printf("Test2()n");
}
~Test2()
{
printf("~Test2()n");
}
};
Test gA("gA");
int main()
{
Test2 t;
return 0;
}
演示结果:
代码语言:javascript复制Test(const char *s) is gA
Test(const char *s) is mA
Test(const char *s) is mB
Test2()
~Test2()
~Test() is mB
~Test() is mA
~Test() is gA
说明:从运行结果,我们可以看到先触发全局对象的构造函数,然后是触发初类Test2里面的mA和mB对象的构造函数,最后 触发局部对象的构造函数,然后进行析构,我们可以看到,析构顺序完全和构造顺序相反。非常类似于栈的操作规则,栈是先入栈,却是最后出栈。
二、const关键字可以修饰类的对象嘛?
1、这个问题答案肯定是可以修饰的,为啥这么说呢?不知大家还是记得c语言里面的struct关键字不,在之前的文章里面也学习过,使用struct关键字也可以来构造类名,只不过他的所有成员都是公开的(public);换句大家好理解的话,就是结构体,那么在c语言里面,const关键字肯定是可以修饰结构体变量的,当然在c 里面肯定也是可以的,c 不是取代c语言的,而是对c语言进行扩展着,并且兼容c语言的。不过const修饰的对象有啥特性呢?
const修饰的对象特性:
- const修饰的对象为只读对象
- 只读对象的成员变量不允许被改变
- 只读对象是编译阶段的概念,运行时无效
代码示例:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi();
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
}
int Test::getMi()
{
return mi;
}
int main()
{
const Test t1(1);
t1.mj = 100;
printf("the t1.mj is %dn",t1.mj);
return 0;
}
运行结果:
代码语言:javascript复制test.cpp: In function ‘int main()’:
test.cpp:34:10: error: assignment of member ‘Test::mj’ in read-only object
t1.mj = 100;
分析:我们可以看出public里面的属性mj是不能修改的,为只读的,因为类对象被const修饰了。
2、const成员函数:
其实以前在写初始化列表的文章之前,以前提到过const修饰的成员属性(不过这个属性是私密的),我们只能用初始化列表对其进行初始化;提这个的原因是;也算是复习一下;同时说的直白一点,咋们刚才讲解完了const修饰的对象,同时用该对象访问的类成员属性是不能修改的;现在我们当然会想到const成员函数了,一环扣一环嘛(我不说,你看到这里也会有好奇心!);下面我们来看一下const成员函数的定义和规则:
(1)定义:
代码语言:javascript复制Type ClassName::function(Type p)const
类中的函数声明与实际函数定义中都必须带const关键字,注意const是函数结尾
(2)规则:
const对象只能调用const的成员函数,不能调用普通的成员函数,例如:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi();
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
}
int Test::getMi()
{
return mi;
}
int main()
{
const Test t1(1);
printf("the mi is %dn",t1.getMi());
return 0;
}
演示结果:
代码语言:javascript复制test.cpp: In function ‘int main()’:
test.cpp:34:42: error: passing ‘const Test’ as ‘this’ argument discards qualifiers [-fpermissive]
printf("the mi is %dn",t1.getMi());
^
test.cpp:23:6: note: in call to ‘int Test::getMi()’
int Test::getMi()
^~~~
我们可以看到const修饰的类对象调用普通成员函数报错,现在我们把普通成员函数getMi()改成const成员函数:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi() const;
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
}
int Test::getMi() const
{
return mi;
}
int main()
{
const Test t1(1);
printf("the mi is %dn",t1.getMi());
return 0;
}
这样的话就可以编译通过了,哈哈。
const成员函数不能直接修改成员变量的值:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi() const;
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
}
int Test::getMi() const
{
mj = 89;
return mi;
}
int main()
{
const Test t1(1);
printf("the mi is %dn",t1.getMi());
return 0;
}
运行结果:
代码语言:javascript复制root@txp-virtual-machine:/home/txp# g test.cpp
test.cpp: In member function ‘int Test::getMi() const’:
test.cpp:25:10: error: assignment of member ‘Test::mj’ in read-only object
mj = 89;
const 成员函数中只能调用 const 成员函数:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi() const;
int getMj() const;
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
}
int Test::getMj() const
{
return mj;
}
int Test::getMi() const
{
getMj();
return mi;
}
int main()
{
const Test t1(1);
printf("the mi is %dn",t1.getMi());
return 0;
}
3、搞清楚成员函数和成员变量是否都是隶属于具体对象
- 从面向对象的角度看,对象由属性(成员变量)和方法(成员函数)构成
- 从程序运行的角度来看,对象由数据和函数构成,而数据可以位于栈、堆、全局数据区;而函数只能位于代码段,而代码段是只读的,在程序运行过程中是不可以被改变的,而对于数据来说,他们处于栈、堆,是可以动态的创建和动态的删除;
4、this指针
不知大家是否注意到在拷贝构造函数里面的这条语句,参数里面是引用,那么t也相当于是一个对象,所以会有t.mi这样的写法,这里要明白:
代码语言:javascript复制Test(const Test& t)
{
mi = t.mi;
}
所以这里我就引出了this指针,他表示方法中的隐藏参数代指当前对象,为了说明这个,下面我们来看一个代码示例:
代码语言:javascript复制#include <stdio.h>
class Test
{
int mi;
public:
int mj;
Test(int i);
Test(const Test& t);
int getMi();
void print();
};
Test::Test(int i)
{
mi = i;
}
Test::Test(const Test& t)
{
mi = t.mi;
}
int Test::getMi()
{
return mi;
}
void Test::print()
{
printf("this = %pn", this);
}
int main()
{
Test t1(1);
Test t2(2);
Test t3(3);
printf("t1.getMi() = %dn", t1.getMi());
printf("&t1 = %pn", &t1);
t1.print();
printf("t2.getMi() = %dn", t2.getMi());
printf("&t2 = %pn", &t2);
t2.print();
printf("t3.getMi() = %dn", t3.getMi());
printf("&t3 = %pn", &t3);
t3.print();
return 0;
}
输出结果:
代码语言:javascript复制root@txp-virtual-machine:/home/txp# ./a.out
t1.getMi() = 1
&t1 = 0x7ffec3f5e750
this = 0x7ffec3f5e750
t2.getMi() = 2
&t2 = 0x7ffec3f5e758
this = 0x7ffec3f5e758
t3.getMi() = 3
&t3 = 0x7ffec3f5e760
this = 0x7ffec3f5e760
分析:
- 在类的成员函数当中,有一个隐含的参数,就是我们刚才说的this指针,它的值就是调用这个函数所对应的对象的地址,说白了,this指针指向当前对象。
- 每一个对象都有一套自己的成员变量,对象和对象之间的成员变量是独立的、不同的;但是每一个对象都共享一个类的成员函数
三、总结:
- 对象的析构顺序与构造顺序相反
- const 关键字能够修饰对象,得到只读对象
- 只读对象只能调用 const 成员函数
- 所有对象共享类的成员函数
- 隐藏的 this 指针用于表示当前对象
好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。我是txp,下期见!