1)在定义的时候必须进行初始化
2)指针可以是const 指针,也可以是指向const对象的指针
3)定义为const的形参,即在函数内部是不能被修改的
4)类的成员函数可以被声明为常成员函数,不能修改类的成员变量
5)类的成员函数可以返回的是常对象,即被const声明的对象
6)类的成员变量是常成员变量不能在声明时初始化,必须在构造函数的列表里进行初始化
note: const如何做到只读?
这些在编译期间完成,对于内置类型,如int, 编译器可能使用常数直接替换掉对此变量的引用。而对于结构体不一定。
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
4)类内的static成员变量属于整个类所拥有,不能在类内进行定义,只能在类的作用域内进行定义
5)类内的static成员函数属于整个类所拥有,不能包含this指针,只能调用static成员函数
static全局变量与普通的全局变量有什么区别:
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
告诉编译器该段代码以C语言进行编译
1)引用是直接访问,指针是间接访问。
2)引用是变量的别名,本身不单独分配自己的内存空间,而指针有自己的独立内存空间
3)引用绑定内存空间(必须赋初值),是一个变量别名不能更改绑定,可以改变对象的值。
总的来说:引用既具有指针的效率,又具有变量使用的方便性和直观性
1) 静态内存分配是在编译时完成的,不占用CPU资源;动态分配内存运行时完成,分配与释放需要占用CPU资源;
2)静态内存分配是在栈上分配的,动态内存是堆上分配的;
3)动态内存分配需要指针或引用数据类型的支持,而静态内存分配不需要;
4)静态内存分配是按计划分配,在编译前确定内存块的大小,动态内存分配运行时按需分配。
5)静态分配内存是把内存的控制权交给了编译器,动态内存把内存的控制权交给了程序员;
6)静态分配内存的运行效率要比动态分配内存的效率要高,因为动态内存分配与释放需要额外的开销;动态内存管理水平严重依赖于程序员的水平,处理不当容易造成内存泄漏。
1)访问寄存器比访问内存单元要快,编译器会优化减少内存的读取,可能会读脏数据。声明变量为volatile,编译器不再对访问该变量的代码优化,仍然从内存读取,使访问稳定。
总结:volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不再编译优化,以免出错。
2)使用实例如下(区分C程序员和嵌入式系统程序员的最基本的问题。):
并行设备的硬件寄存器(如:状态寄存器)
一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
多线程应用中被几个任务共享的变量
3)一个参数既可以是const还可以是volatile吗?解释为什么。
可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
4)一个指针可以是volatile 吗?解释为什么。
可以。尽管这并不很常见。一个例子当中断服务子程序修该一个指向一个buffer的指针时。
下面的函数有什么错误:
int square(volatile int *ptr) {
return *ptr * *ptr;
}
下面是答案:
这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr){
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr){
int a;
a = *ptr;
return a * a;
}
class String{
public:
//普通构造函数
String(const char *str = NULL);
//拷贝构造函数
String(const String &other);
//赋值函数
String & operator=(String &other) ;
//析构函数
~String(void);
private:
char* m_str;
};
分别实现以上四个函数
//普通构造函数
String::String(const char* str){
if(str==NULL) //如果str为NULL,存空字符串{
m_str = new char[1]; //分配一个字节
*m_str = ‘\0′; //赋一个’\0′
}else{
str = new char[strlen(str) + 1];//分配空间容纳str内容
strcpy(m_str, str); //复制str到私有成员m_str中
}
}
//析构函数
String::~String(){
if(m_str!=NULL) //如果m_str不为NULL,释放堆内存{
delete [] m_str;
m_str = NULL;
}
}
//拷贝构造函数
String::String(const String &other){
m_str = new char[strlen(other.m_str)+1]; //分配空间容纳str内容
strcpy(m_str, other.m_str); //复制other.m_str到私有成员m_str中
}
//赋值函数
String & String::operator=(String &other){
if(this == &other) //若对象与other是同一个对象,直接返回本{
return *this
}
delete [] m_str; //否则,先释放当前对象堆内存
m_str = new char[strlen(other.m_str)+1]; //分配空间容纳str内容
strcpy(m_str, other.m_str); //复制other.m_str到私有成员m_str中
return *this;
}
(1)定义类差别
(2)继承差别
note: 主要点就两个:默认的访问级别和默认的继承级别 class都是private
多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。
动态多态实现有几个条件:(1) 虚函数; (2) 一个基类的指针或引用指向派生类的对象;
基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表。虚函数表的地址在每个对象的首地址。查找该虚函数表中该函数的指针进行调用。每个对象中保存的只是一个虚函数表的指针,C++内部为每一个类维持一个虚函数表,该类的对象的都指向这同一个虚函数表。虚函数表中为什么就能准确查找相应的函数指针呢?因为在类设计的时候,虚函数表直接从基类也继承过来,如果覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,因此可以根据指针准确找到该调用哪个函数。
编译器为每一个类维护一个虚函数表,每个对象的首地址保存着该虚函数表的指针,同一个类的不同对象实际上指向同一张虚函数表。
静态多态是指通过模板技术或者函数重载技术实现的多态,其在编译器确定行为。动态多态是指通过虚函数技术实现在运行期动态绑定的技术。
纯虚函数如何定义,为什么对于存在虚函数的类中析构函数要定义成虚函数?
为了实现多态进行动态绑定,将派生类对象指针绑定到基类指针上,对象销毁时,如果析构函数没有定义为析构函数,则会调用基类的析构函数,显然只能销毁部分数据。如果要调用对象的析构函数,就需要将该对象的析构函数定义为虚函数,销毁时通过虚函数表找到对应的析构函数。
析构函数能抛出异常吗?
(1) 如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
(2) 通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题
面向对象的三个基本特征,并简单叙述之?
重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
多态的作用?
虚函数与纯虚函数区别
纯虚函数如何定义?含有纯虚函数的类称为什么?为什么析构函数要定义成虚函数?
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。纯虚函数是虚函数再加上= 0。virtual void fun ()=0。含有纯虚函数的类称为抽象类在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显