头一次记录技术相关知识,相信将来一定能看到自己的进步!
最近忙着找工作,因为要应对各种笔试。所以做了几个练习,不练不知道一练吓一跳。
发现自己对C++的基础知识几乎忘光了。突然感慨自己复制粘贴好多年的结果。。。
1.关于构造和析构顺序:
include "stdafx.h"
#include <iostream>
using namespace std;
class One
{
public:
One(){cout<<"One ";}
~One(){cout<<"~One ";}
};
class Two
{
public:
Two(int j){cout<<" Two ";}
~ Two(){cout<<"~ Two ";}
};
class Three
{
public:
Three(){cout<<"Three ";}
virtual ~Three(){cout<<"~Three ";}
};
class Four : public Three
{
public:
Four (int j)
{
cout<<"Four ";
pb = new Two(j);
}
~Four ()
{
delete pb;
cout<<"~Four ";
}
private:
Two *pb;
One a;
};
int _tmain(int argc, _TCHAR* argv[])
{
Three* pc = new Four(1);
delete pc;
return 0;
}
Three One Four Two ~Two ~Four ~One ~Three
这个例子主要是说基类虚析构函数的作用。
从答案可以看出来,构造的顺序是基类,子类的成员对象,子类。
如果没有 pb = new Two(j)这句,只有Two *pb;则根本不会调用Two 的构造函数(突然觉得自己很傻,这根本就是显然是的事却犹豫了一下。。。)
如果class Three没有声明虚析构函数的话,则调用Delete pc语句的时候,不会调用其他的析构函数。
同样如果_tmain函数中的语句改为 Four* pc = new Four(1)
此时class three中是否为虚析构函数就无所谓了,delete时会按顺序调用其他类的析构函数。
总结一下,基类中的虚析构函数主要是用于防范上转型对象上(自己这么觉得)。
delete上转型对象时,如果此时基类的析构函数是虚函数,则顺序调用子类对象关联的其他类的析构函数。
如果此时基类的析构函数不是虚函数,则只调用基类的析构函数,这就为内存泄漏留下了隐患。
2. 关于Sizeof:
#include <iostream>
class A
{
public:
A(){}
A(int a,unsigned int b):m_a(a),m_b(b),m_c(){}
public:
int m_a;
unsigned int m_b;
A* m_c;
};
class B : public A
{
public:
B(){}
public:
static int m_d;
};
int _tmain(int argc, _TCHAR* argv[])
{
A* a;
B b;
std::cout<<sizeof(A)<<" ";
std::cout<<sizeof(b)<<" ";
std::cout<<sizeof(a)<<" ";
a = new B[2];
std::cout<<sizeof(a)<<" ";
return 0;
}
答案是12 12 4 4
一直以为unsigned int 的大小是2,今天试了一下原来是4。
再者因为a是指针所以a指向的所有东西sizeof都是4。不要被a = new B[2]所迷惑。
那天也遇到一个类似的问题,如果单纯的一个空类什么变量也没有,这个空类的sizeof是1(具体为什么是1还没搞清楚。~~老中医的Style,拿来就用嘛)
再者如果类中有静态参数,静态参数是不占空间的,估计是因为静态参数的存储不再类内的原因。初始化也不再类内。
3. 还是构造函数
#include <iostream>
class A
{
public:
A(int n=0):m_i(n){std::cout<<m_i;}
protected:
int m_i;
};
class B : public A
{
public:
B(int n):m_j(n),m_a(--m_j),m_b(){std::cout<<m_j;}
private:
int m_j;
A m_a;
A m_b;
static A m_c;
};
int _tmain(int argc, _TCHAR* argv[])
{
B b(2);
std::cout<<std::endl;
return 0;
}
答案是0101
这道题很诡异也是很好的一道题,一开始觉得答案肯定错了,一运行答案真的没错。。。
这道题主要是考察类的构造顺序和成员变量的初始化顺序。
第一个输出0是因为首先调用基类的构造函数,基类中默认参数是0。
第二个输出1是因为执行A m_a;时调用了A的构造函数,
由B的初始化列表B(int n):m_j(n),m_a(--m_j)可以看出首先对m_j赋值为2,
接着m_a(--m_j)将1传给A的构造函数所以输出1.
第三个输出0是因为A m_b;调用a的构造函数时没有传入参数,所以使用默认参数0.
第四个输出1是因为最后要调用B的构造函数,虽然传入的是2,但是之前的--m_j使m_j的值变成了1。