1. 局部变量
形参和函数内部定义的变量统称为局部变量。所以认为只在函数内部定义的变量才是局部变量是错误的。
2. #include <>
和#include ""
的区别是什么?
在C程序中包含文件有以下两种方法:
(1) 用符号“<”和“>”将要包含的文件的文件名括起来。这种方法指示预处理程序先到预定义的缺省路径下寻找文件,到再当前目录下寻找文件。预定义的缺省路径通常是在INCLUDE环境变量中指定的,请看下例:
INCLUDE=C:\COMPILER\INCLUDE;S:\SOURCE\HEADERS;
对于上述INCLUDE
环境变量,如果用#include<file>
语句包含文件,编译程序将首先到C:\COMPILER\INCLUDE
目录下寻找文件;如果未找到,则到S:\SOURCE\HEADERS
目录下继续寻找;如果还未找到,则到当前目录下继续寻找。
(2) 用双引号将要包含的文件的文件名括起来。这种方法指示预处理程序先到当前目录下寻找文件,再到预定义的缺省路径下寻找文件。
对于上例中的INCLUDE环境变量,如果用#include“file”
语句包含文件,编译程序将首先到当前目录下寻找文件;如果未找到,则到C:\COMPILER\INCLUDE
目录下继续寻找;如果还未找到,则到S:\SOURCE\HEADERS
目录下继续寻找。
#include<file>
语句一般用来包含标准头文件(例如stdio.h或stdlib.h),因为这些头文件极少被修改,并且它们总是存放在编译程序的标准包含文件目录下。#include“file”
语句一般用来包含非标准头文件,因为这些头文件一般存放在当前目录下,你可以经常修改它们,并且要求编译程序总是使用这些头文件的最新版本。
3. 数组指针和指针数组的区别
可参考资料:
数组指针和指针数组的区别
二维数组作为函数参数传递剖析(C语言)(6.19更新第5种)
4. 类的成员函数中是否可以调用delete this
?
- 在类的成员函数中可以调用
delete this
。
假设类中有个成员函数被称为release
,在release
方法内我们调用了语句release this
.我们在调用此语句之后,是否还可以进行其他的操作?
同样也是可以的, 但是要求此后所做的操作(包括执行语句或者调用其他的成员函数)都将不会再涉及到这个对象的数据成员和虚函数. 根本原因在于delete操作符的功能和类对象的内存模型. 当一个类对象声明时, 系统会为其分配内存空间. 在类对象的内存空间中,只有数据成员和虚函数表指针, 并不包括代码内容, 类的成员函数被单独放在代码段中. 在调用成员函数时, 都会隐含传送一个this指针, 根据this指针来决定是哪个对象来调用相应成员函数. 因此,当调用delete this
时,类对象的内存空间被释放。在delete this
之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。 delete this
之后不是释放了类对象的内存空间了么,那么这段内存应该已经还给系统,不再属于这个进程。照这个逻辑来看,应该发生指针错误,无访问权限之类的令系统崩溃的问题才对啊?这个问题牵涉到操作系统的内存管理策略。delete this释放了类对象的内存空间,但是内存空间却并不是马上被回收到系统中,可能是缓冲或者其他什么原因,导致这段内存空间暂时并没有被系统收回。此时这段内存是可以访问的,你可以加上100,加上200,但是其中的值却是不确定的。当你获取数据成员,可能得到的是一串很长的未初始化的随机数;访问虚函数表,指针无效的可能性非常高,造成系统崩溃。
- 如果在类的析构函数中调用delete this,会发生什么?
实验告诉我们,会导致堆栈溢出。原因很简单,delete的本质是“为将被释放的内存调用一个或多个析构函数,然后,释放内存”。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。
- 除此之外, 如果成员函数是虚函数也是可以被正确的调用的, 因为在对象的内存空间被销毁之前, 我们就已经确定好了虚函数的代码段位置. 所以可以正确的执行, 但是同样要注意在对象被析构以后就不能再涉及到这个对象的数据成员和虚函数.
#include <iostream>
#include <iostream>
using namespace std;
class Base
{
public:
void virtual foo()
{
cout << "Base::foo()" << endl;
}
~Base()
{
cout << "delete Base" << endl;
}
};
class myClass:public Base
{
public:
void virtual foo(){
delete this;
cout << "myClass::foo() " << endl;
}
~myClass()
{
cout << "delete myClass" << endl;
}
};
void func()
{
Base *a = new myClass();
a->foo();
}
int main()
{
func();
return 0;
}
//输出
delete myClass
delete Base
myClass::foo()
所以,最后总结: 一, 在成员函数中调用delete this;没有问题,但是在析构函数中调用则会由于delete this会自动调用析构函数的原因造成无限递归,导致栈溢出。二,调用完delete this 之后,对象的内存空间被释放了,导致不能再访问数据成员和虚函数.
5. 关于大小端存储
测试系统是大小端模式的代码
bool checkSystem()
{
union check;
{
char ch;
int i ;
} c;
c.i = 1;
return (c.ch == 1);
}
如果是大端模式, 则返回false. 如果是小端模式, 则返回true.
1. RAII 编程技术
RAII(资源获取即初始化),是C++编程时非常常见且有用的一个特性。
是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。所以,通过对象来管理资源,能够保证即使异常发生的时候也可以通过对象的析构函数来关闭资源。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。
2. RAII handle
每一个明确的资源配置动作,都应该在单一语句中执行,并且在该语句中立刻将配置获得的资源交给Handle进行管理(如 shared\_ptr),程序中一般不直接出现delete。用shared\_ptr来管理共享资源。
3.
include
summer@localhost iVMS-4200.gch]$ ulimit -s
8192
4. 使用匿名空间来代替static实现将数据代码封装在一个源文件中使用
static的功能较多,违反了一个关键字只有一个含义的原则。如果用static限制或者函数只在一个源文件中使用,不仅比较繁琐,还有可能会带来许多理解上的困难。相比匿名空间,它具有比较纯粹的功能。只需要将源文件代码放到namespace{…},大括号内部,就实现了源文件名称的独立性。除此之外,static关键字有一些限制,如不适用于是类型定义。而匿名的名字空间则没有这些问题。
5. inline函数可以用来修饰普通函数
6. 全局对象的初始化。
C++保证进入main函数之前完成全局对象的构造,包括namespace级全局对象,文件级静态对象,class静态对象,但不包括函数内的静态对象。
7. exit不是线程安全的
exit函数用在多线程程序中,可能会出现一些问题。exit函数在退出的时候会调用atexit()注册的函数(出口函数),调用cleanup()关闭所有的流,最后调用_exit()函数终止进程。exit函数在调用的时候,除了终止进程,还会析构全局对象和已经构造好了的函数静态对象。因而,一个线程调用exit后,可能会使得另一个线程无法使用全局对象而导致问题。
_exit函数只会终止线程,不会导致全局对象的析构,但是也同时不会做任何清理工作,比如fflush标准输出。