1.const变量和宏的区别?
(1)宏是在预编译器直接在使用位置将代码直接展开插入的,不进行类型正确性检查,由于仅仅是单纯的展开替换,不会分配内存;而const常量具有具体的类型,编译阶段会严格执行类型检查,并且是一个编译期的常量,具体是否分配空间视情况而定,对于只声明未使用的const常量不分配内存,在编译期遇到有使用const常量时才会分配内存,并将常量名称保存在符号表中;
1. http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html ———C/C++中const关键字详解
C++常量折叠:
常量折叠表面上的效果和宏替换是一样的,只是,“效果上是一样的”,而两者真正的区别在于,宏是字符常量,在预编译完宏替换完成后,该宏名字会消失,所有 对宏如PI的引用已经全部被替换为它所对应的值,编译器当然没有必要再维护这个符号。而常量折叠发生的情况是,对常量的引用全部替换为该常量的值,但 是,常量名并不会消失,编译器会把他放入到符号表中,同时,会为该变量分配空间,栈空间或者全局空间,常量折叠说的是,在编译阶段,对该变量进行值替换,同时,该常量拥有自己的内存空间,并非像宏定义一样不分配空间:
http://blog.csdn.net/yby4769250/article/details/7359278
2.函数重载可以发生在不同类间么?
答:这个更准确的说是要在同一作用域中,对于普通函数的重载要求必须在一作用域中,对于成员函数重载一定是在同一类作用域中,不能在不同类间;不同类间只会存在覆盖(重写),隐藏,这两个概念上课时讲过,不清楚的查笔记复习下,需要强调的是隐藏要慎用,需要隐藏的函数其实大部分可以通过覆盖解决
3.析构函数能否被子类继承?
构造函数和析构函数均不可以被子类继承,只要明确析构函数和和构造函数的用途,这个问题也就清楚了;逆向的想一下也可以得到答案,假如可以被继承,现在A是基类,B是子类,现在认为B继承了A的构造函数,就意味着我们可以用A的构造函数来构造一个B的对象,这不是自相矛盾么;当然这里说不能被继承并不意味着不能被调用,构造函数的调用顺序是先调用基类,再调用子类,析构顺序反之;
4.函数在return一个对象时会分配一个临时空间么?
答:分配,这个上课的时候讲的是对的;但是由于不同编译器进行优化策略不一样,所以对应的构造函数(拷贝构造)调用的次数可能会减少,但是不影响对象即程序运行的正确性
执行附件中的代码分析原因
1.const变量和宏的区别?
答:
(1)宏是在预编译器直接在使用位置将代码直接展开插入的,不进行类型正确性检查,由于仅仅是单纯的展开替换,不会分配内存;而const常量具有具体的类型,编译阶段会严格执行类型检查,并且是一个编译期的常量,具体是否分配空间视情况而定,对于只声明未使用的const常量不分配内存,在编译期遇到有使用const常量时才会分配内存,并将常量名称保存在符号表中;
1. http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html ———C/C++中const关键字详解
C++常量折叠:
常量折叠表面上的效果和宏替换是一样的,只是,“效果上是一样的”,而两者真正的区别在于,宏是字符常量,在预编译完宏替换完成后,该宏名字会消失,所有 对宏如PI的引用已经全部被替换为它所对应的值,编译器当然没有必要再维护这个符号。而常量折叠发生的情况是,对常量的引用全部替换为该常量的值,但 是,常量名并不会消失,编译器会把他放入到符号表中,同时,会为该变量分配空间,栈空间或者全局空间,常量折叠说的是,在编译阶段,对该变量进行值替换,同时,该常量拥有自己的内存空间,并非像宏定义一样不分配空间:
http://blog.csdn.net/yby4769250/article/details/7359278
2.函数重载可以发生在不同类间么?
答:这个更准确的说是要在同一作用域中,对于普通函数的重载要求必须在一作用域中,对于成员函数重载一定是在同一类作用域中,不能在不同类间;不同类间只会存在覆盖(重写),隐藏,这两个概念上课时讲过,不清楚的查笔记复习下,需要强调的是隐藏要慎用,需要隐藏的函数其实大部分可以通过覆盖解决
3.析构函数能否被子类继承?
答:构造函数和析构函数均不可以被子类继承,只要明确析构函数和和构造函数的用途,这个问题也就清楚了;逆向的想一下也可以得到答案,假如可以被继承,现在A是基类,B是子类,现在认为B继承了A的构造函数,就意味着我们可以用A的构造函数来构造一个B的对象,这不是自相矛盾么;当然这里说不能被继承并不意味着不能被调用,构造函数的调用顺序是先调用基类,再调用子类,析构顺序反之;
4.函数在return一个对象时会分配一个临时空间么?
答:分配,这个上课的时候讲的是对的;但是由于不同编译器进行优化策略不一样,所以对应的构造函数(拷贝构造)调用的次数可能会减少,但是不影响对象即程序运行的正确性
执行附件中的代码分析原因
5.虚函数是通过虚指针找到虚函数地址并执行的,那普通成员函数是怎么查找到对应的函数地址的呢?
答:想要了解这个问题,需要先了解下预编译,编译,汇编,链接的过程;
预编译:
宏展开,删除#define,处理条件编译指令,将#include的头文件插入到预编译指令的位置(这个可能会有递归包含过程),去掉代码中的注释,添加行号和文件名标示;(.i文件)
编译:进行词法,语法,语义分析以及优化,生成汇编代码文件(.s文件)
汇编:根据汇编指令和机器指令对照表,将汇编代码翻译成机器指令(.o/.obj文件)
链接:将对应的目标文件中的指令按照相似段进行合并,并分配对应的地址和空间
链接完毕后可执行文件生成,程序要调用某个成员函数,首先得要被装载到内存中,然后系统会根据编译时给出的虚拟地址和偏移量计算出得到每个函数在内存中的地址,所以在函数调用处会根据函数名查找符号表得到对应函数的地址,即找到对应函数
注:上述只是粗略介绍,想要更深入了解,可以查看《程序员的自我修养》一书
6.如何正确使用虚析构函数?
答:如果一个类不需要被继承,那么析构函数就没必要声明成虚函数,当被设计成基类时,最好声明成虚析构函数,这样基类的指针指向派生类对象,用基类的指针回收子类部分的空间,避免内存泄露,为什么声明成虚析构函数就可以用基类的指针释放子类部分的空间呢?因为编译器看见析构函数是虚函数,就会自动调用指针指向的实际类型的析构函数,可以运行下面的小例子,验证一下
#include <iostream>
using namespace std;
class Base
{
public:
Base(){cout<<"Base..."<<endl;}
// ~Base(){cout<<"~Base..."<<endl;}
virtual ~Base(){cout<<"~Base..."<<endl;}
};
class Child:public Base
{
public:
Child(){cout<<"Child..."<<endl;}
// ~Child(){cout<<"~Child..."<<endl;}
virtual ~Child(){cout<<"~Child..."<<endl;}
};
int main()
{
Base a;
Child b;
Base *p = new Base();
delete p;
p = NULL;
p = new Child();
delete p; //当析构函数非虚时,看看是否调用子类的析构函数
p = NULL;
Child *q = new Child();
delete q; //
q = NULL;
// q = new Base();
// delete q;
// q = NULL;
return 0;
}
5.虚函数是通过虚指针找到虚函数地址并执行的,那普通成员函数是怎么查找到对应的函数地址的呢?
想要了解这个问题,需要先了解下预编译,编译,汇编,链接的过程;
预编译:
宏展开,删除#define,处理条件编译指令,将#include的头文件插入到预编译指令的位置(这个可能会有递归包含过程),去掉代码中的注释,添加行号和文件名标示;(.i文件)
编译:进行词法,语法,语义分析以及优化,生成汇编代码文件(.s文件)
汇编:根据汇编指令和机器指令对照表,将汇编代码翻译成机器指令(.o/.obj文件)
链接:将对应的目标文件中的指令按照相似段进行合并,并分配对应的地址和空间
链接完毕后可执行文件生成,程序要调用某个成员函数,首先得要被装载到内存中,然后系统会根据编译时给出的虚拟地址和偏移量计算出得到每个函数在内存中的地址,所以在函数调用处会根据函数名查找符号表得到对应函数的地址,即找到对应函数
注:上述只是粗略介绍,想要更深入了解,可以查看《程序员的自我修养》一书
6.如何正确使用虚析构函数?
如果一个类不需要被继承,那么析构函数就没必要声明成虚函数,当被设计成基类时,最好声明成虚析构函数,这样基类的指针指向派生类对象,用基类的指针回收子类部分的空间,避免内存泄露,为什么声明成虚析构函数就可以用基类的指针释放子类部分的空间呢?因为编译器看见析构函数是虚函数,就会自动调用指针指向的实际类型的析构函数,可以运行下面的小例子,验证一下
#include <iostream>
using namespace std;
class Base
{
public:
Base(){cout<<"Base..."<<endl;}
// ~Base(){cout<<"~Base..."<<endl;}
virtual ~Base(){cout<<"~Base..."<<endl;}
};
class Child:public Base
{
public:
Child(){cout<<"Child..."<<endl;}
// ~Child(){cout<<"~Child..."<<endl;}
virtual ~Child(){cout<<"~Child..."<<endl;}
};
int main()
{
Base a;
Child b;
Base *p = new Base();
delete p;
p = NULL;
p = new Child();
delete p; //当析构函数非虚时,看看是否调用子类的析构函数
p = NULL;
Child *q = new Child();
delete q; //
q = NULL;
// q = new Base(); //这里是否正确,请分析
// delete q;
// q = NULL;
return 0;
}