c++常见内存错误

原创 2006年05月24日 09:25:00


在C和C + +中,s t a t i c都有两种基本的含义,并且这两种含义经常是互相有冲突的:
1) 在固定的地址上分配,也就是说对象是在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的。这也是静态存储的概念。
2) 对一个特定的编译单位来说是本地的(就像我们在后面将要看到的,这在C + +中包括类的范围)。这里s t a t i c控制名字的可见性,所以这个名字在这个单元或类之外是不可见的。这也描述了连接的概念,它决定连接器将看到哪些名字。
1. 不属于类的静态成分

1.1. 静态变量


1.1.1. 修饰局部变量
  这个变量作用域被局限于函数体内,然而和非static局部变量不同,这个变量的值被保留下来,而不会每次调用这个函数的时候都被初始化。

void fun( void ){
  int var = 0;
  static int svar = 0;
 
  var ++;
  svar++
}

函数fun在第一次被调用后var , svar的值都变成了1,而在第二次被调用后,var依然等于1 ,而 svar的值变成了2。


1.1.2. 静态全局变量
  全局变量默认的是静态的(无需用static修饰)
file1.cpp
  int gi ;

gi是全局变量,它自动是静态存储的。

1.1.2.1. 用static修饰修饰全局变量
把这个全局变量局限在本文件中(默认情况下全局变量可以被任何文件使用)
file1.cpp
  int gi ;
file2.cpp
  extern int gi ;

如果用static 来修饰file1.cpp 中的gi ( static int gi ;),则在file2.cpp中就不能使用gi了

1.2. 静态函数
  static关键字使得这个函数只能被本文件里的其他函数调用。

 

 

2. 属于类的静态成分
2.1. 静态类成员(变量)

态数据成员被当作该类类型的全局对象对于非静态数据成员每个类对象都有自己的拷贝而静态数据成员对每个类类型只有一个拷贝静态数据成员只有一份由该类类型的所有对象共享访问。
   静态数据成员在该类定义之外被初始化。与全局对象一样对于静态数据成员在程序中也只能提供一个定义这意味着静态数据成员的初始化不应该被放在头文件中而应该放在含有类的非inline 函数定义的文件中。
  静态数据成员可以是各种数据类型(预定议数据类型和用户数据类型)。
  静态数据成员可以是const,也可以是非const。
2.1.1. 非const静态类成员


头文件
class CB{
public:
    CB(){  mi = 2; }
 CB(int i){  mi = i; }
    int mi;
};

class CA{
public:
    CA(){  mi = 3; }
    int mi;
 static int smi;
 static int smi2;
 static CB ob;
 static CB ob2;
};

实现文件

 int CA::smi;   //不能写成int CA::smi();
 int CA::smi2( 5 );
 CB CA::ob;  //不能写成 CB CA::ob() ;
 CB CA::ob2(5);

int main(int argc, char* argv[])
{
 int i=0;
 int j=0;

 i = CA::smi;
 i = CA::smi2;
 j = CA::ob.mi;
 j = CA::ob2.mi;

 return 0;
}

2.1.2. const静态类成员

头文件
#include <string>
using namespace std;

class CB{
public:
    CB(){  mi = 2; }
 CB(int i){  mi = i; }
    int mi;
};

class CA{
public:
    CA(){  mi = 3; }
    int mi;

 static const CB ob;
 static const CB ob2;
};


实现文件

const CB CA::ob;
const CB CA::ob2(5);

int main(int argc, char* argv[])
{
 int j=0;
 string name;

 name = CA::name;
 name = CA::name2;
 j = CA::ob.mi;
 j = CA::ob2.mi;

 return 0;
}


2.1.3. 访问静态类成员
  2种方式: 直接访问和通过类对象访问.

  直接访问
即用被类名限定修饰的名字直接访问它。如上例子CA::name;
  类对象访问
  CA  oa;
oa. name;
  或者
CA*  poa;
poa-> name;

2.1.4. static数据成员的一个特殊用途
static数据成员的类型可以是其所属类而非static 数据成员只能被声明为该类的对象的指针或引用.

//-----------头文件
class CA{
public:
 static CA * instance() { return & soa; }
private:
 CA(int i) : mi(i) {};
private:
 int mi;
    static CA soa;
};

//-----------cpp文件
CA CA:: soa(5);

这是因为static 数据成员并不占据类对象的存储空间。所以 sizeof CA的返回值为4
上面这个例子利用这个特性,很简单的就实现了单例模式.

2.1.5. 注意
1.  局部类不可以有static成员变量(但嵌套类可以有)
//--------局部类
void fun(){
  class foo {
    static int mi;  //错误

}
}

//--------嵌套类
class outer{
  class inner {
    static int mi;  //正确

}
}

2. 当使用static const int 类成员来定义类成员数组时,要看当前编译器是否支持这个特性。

class CA{
public:
  int mi;
  static const int size;
  char sz[ size ];
};

 

2.2. 静态成员函数
在一个成员函数前加上static关键字就使得这个成员函数成为类的静态成员函数
静态成员函数有几个特别的地方:
  不能声明为const 或volatile
  没有this 指针因此在静态成员函数中隐式或显式地引用这个指针都将导致编译时刻错误
  不能访问非静态数据成员

2.2.1. 访问静态类成员函数
3种方式: 直接访问和通过类对象访问.
class CA{
public:
    CA(){  mi = 3; }
    int mi;
  static int fun();
};

int CA::fun(){
 return 9;
 }

  直接访问

int i = CA:: fun();

  类对象访问
 CA  oa;
int i = oa. fun();
  或者
CA*  poa;
poa-> fun();

  通过函数指针来访问
 int (*pfn)();

 pfn = CA::fun;
 i = pfn();
或者
 pfn = oa.fun;
 i = pfn();


3. 静态对象的内存分配

3.1. 函数内部的静态变量
  C和C + +都允许在函数内部创建一个s t a t i c对象,这个对象将存储在程序的静态数据区中,而不是在堆栈中。这个对象只在函数第一次调用时初始化一次,以后它将在两次函数之间保持它的值。

3.2. 全局静态对象
   在C++中,全局静态对象的构造函数是在m a i n ( )之前调用的,所以我们现在有了一个在进入m a i n ( )之前执行一段代码的简单的、可移植的方法,并且可以在退出m a i n ( )之后用析构函数执行代码。在C中要做到这一点,我们不得不熟悉编译器开发商的汇编语言的开始代码。


3.3. 静态对象的析构函数
静态对象的析构函数(包括静态存储的所有对象,不仅仅是上例中的局部静态变量)在程序从main() 块中退出时,或者标准的C库函数e x i t ( )被调用时才被调用。多数情况下m a i n ( )函数的结尾也是调用e x i t ( )来结束程序的。这意味着在析构函数内部使用e x i t ( )是很危险的,因为这可能陷入一个死循环中。但如果用标准的C库函数a b o r t ( )来退出程序,静态对象的析构函数并不会被调用。
我们可以用标准C库函数a t e x i t ( )来指定当程序跳出m a i n ( )(或调用e x i t ( ))时应执行的操作。
在这种情况下,在跳出m a i n ( )或调用e x i t ( )之前,用a t e x i t ( )注册的函数可以在所有对象的析构函数之前被调用。
静态对象的销毁是按它们初始化时相反的顺序进行的。当然只有那些已经被创建的对象才会被销毁。幸运的是,编程系统会记录对象初始化的顺序和那些已被创建的对象。全局对象总是在m a i n ( )执行之前被创建了,所以最后一条语句只对函数局部的静态对象起作用。如果一个包含静态对象的函数从未被调用过,那么这个对象的构造函数也就不会执行,这样自然也不会执行析构函数。


C++常见内存错误详解

  • 2009年08月31日 14:56
  • 71KB
  • 下载

C++常见内存错误汇总

一、前言 从事自动化测试平台开发的编程实践中,遭遇了几个程序崩溃问题,解决它们颇费了不少心思,解决过程中的曲折和彻夜的辗转反侧却历历在目,一直寻思写点东西,为这段难忘的经历留点纪念,总结惨痛的教训带...

C/C++常见指针错误 and 内存访问越界

1) 内存分配未成功,却使用了它。    编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入...

C++语言--指针--3.1--什么是地址、用指针来保存内存地址、为什么使用指针、指针的常见错误

前言:C++系列之指针! 1.指针 指针是用来保存内存地址的变量。 2.用指针来保存地址       2.1   指针与类型变量 从上面我们可以看到指针P1指向的a为int类型,指针P2...

【C++语言99个常见编程错误】第6章 内存和资源管理问题

第6章 内存和资源管理问题 未能区分纯量和数组的内存分配机制 w = new W(arg);//纯量 w = new W[n]; //数组 new运算符的行为不可以通过重载来改变,它总是会调用oper...

C++中常见的内存错误

发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,...

C++常见的内存错误及其对策

发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任 ......

C++内存分配方式及常见错误

内存分配方式有三种:   (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。   (2) 在栈上创建。在执行函数时,...

C++常见内存错误汇总

C++中内存错误通常属于运行时错误,只有在程序运行时才能发现,编译器无法自动检测到内存错误。多数情况下是程序逻辑或者参数存在某些错误。下面总结一下C++常见的内存错误: 1. 内存泄露 内...
  • Aoutlaw
  • Aoutlaw
  • 2017年04月19日 20:31
  • 148

基于C++中常见内存错误的总结

在系统开发过程中出现的bug相对而言是比较好解决的,花费在这个上面的调试代价不是很大,但是在系统集成后的bug往往是难以定位的bug(最好方式是打桩,通过打桩可以初步锁定出错的位置,如:进入函数前打印...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c++常见内存错误
举报原因:
原因补充:

(最多只允许输入30个字)