c++ 语法

C和C++怎么分配和释放内存,区别是什么

C: malloc  free   //函数  calloc() 会初始化  realloc 会重置大小

C++:   new  delete  //运算符

C++中内存分为5个区: 堆 栈 全局/静态区 常量区 自由存储区(也有说代码区)

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可以用于申请动态内存和释放内存。

对于非内部数据类型对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于

malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free.


c++中delete和delete[]的区别

C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。

请看下面的程序。

#include <iostream>;
using namespace std;
 
class T {
public:
  T() { cout << "constructor" << endl; }
  ~T() { cout << "destructor" << endl; }
};
 
int main()
{
  const int NUM = 3;
 
  T* p1 = new T[NUM];
  cout << hex << p1 << endl;
  //  delete[] p1;
  delete p1;
 
  T* p2 = new T[NUM];
  cout << p2 << endl;
  delete[] p2;
}

从运行结果中我们可以看出,delete p1 在回收空间的过程中,只有 p1[0] 这个对象调用了析构函数,其它对象如 p1[1]、p1[2] 等都没有调用自身的析构函数,这就是问题的症结所在。如果用 delete[],则在回收空间之前所有对象都会首先调用自己的析构函数。
    基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。
    所以一个简单的使用原则就是:new 和 delete、new[] 和 delete[] 对应使用。


原因如下,事实上new和delete都是两步操作,分配(释放)堆内存和调用构造(析构)函数
对于堆内存的释放,确实不需要做额外的事情,delete和delete []无差别
区别就在于调用析构函数这一步,如果用delete只会调用第一个对象的析构,只有调用delete[]才会调用全部的析构



c++中的this指针与静态成员函数

提出问题:我们知道为了共享存储空间,多个对象会共享一组成员函数,编译器不会为每个对象都存储一个成员函数,而是将类的成员函数存储在某个单独的空间。(注意:逻辑上成员函数是属于对象的,因为虽然函数体相同,但是其中访问的数据成员是不同的,故为了节约空间所以才采取了这种物理实现)这种存储策略就会带来一个问题,当对象调用成员函数时,若此成员函数访问了对象的数据成员,成员函数如何知道该数据成员属于哪个对象?

解决问题:C++编译器是这样来做的,编译器为每个成员函数隐性地添加一个类的指针类型的参数(如A *this),当对象在成员函数中调用成员函数时,编译器会将该对象的首地址传递给该成员函数,这样下面的调用
A   a;
a.fun()
{
  cout<<value<<endl;
}
实际上就成了
a.fun()
{
  cout<<this->value<<endl;
}
那this指针又和静态成员函数有什么联系呢?
静态成员函数和静态数据成员的性质相似,它们都是独立于对象而属于类的。静态成员函数不属于对象,所以它没有this指针,没有了this指针那么静态成员函数也就无法访问类的非静态数据成员。
实际上,静态成员函数之所以诞生,是用来访问静态数据成员的!


C++ 类的静态成员详细讲解

在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。

静态成员的定义或声明要加个关键static。静态成员可以通过双冒号来使用即<类名>::<静态成员名>。

 

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象。希望阅读本文可以使读者对类的静态成员变量和成员函数有更为深刻的认识。

 

第一个例子,通过类名调用静态成员函数和非静态成员函数

class Point
{
public:	
	void init()
	{  
	}
	static void output()
	{
	}
};
void main()
{
	Point::init();
	Point::output();
}

编译出错:error C2352: 'Point::init' : illegal call of non-static member function

结论1:不能通过类名来调用类的非静态成员函数。

 

第二个例子,通过类的对象调用静态成员函数和非静态成员函数

将上例的main()改为:

void main()
{
	Point pt;
	pt.init();
	pt.output();
}

编译通过。

结论2:类的对象可以使用静态成员函数和非静态成员函数。

 

第三个例子,在类的静态成员函数中使用类的非静态成员

#include <stdio.h>
class Point
{
public:	
	void init()
	{  
	}
	static void output()
	{
		printf("%d\n", m_x);
	}
private:
	int m_x;
};
void main()
{
	Point pt;
	pt.output();
}

编译出错:error C2597: illegal reference to data member 'Point::m_x' in a static member function

因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了,就好比没有声明一个变量却提前使用它一样。

结论3:静态成员函数中不能引用非静态成员。

 

第四个例子,在类的非静态成员函数中使用类的静态成员

class Point
{
public:	
	void init()
	{  
		output();
	}
	static void output()
	{
	}
};
void main()
{
	Point pt;
	pt.output();
}

编译通过。

结论4:类的非静态成员函数可以调用用静态成员函数,但反之不能。

 

第五个例子,使用类的静态成员变量

#include <stdio.h>
class Point
{
public:	
	Point()
	{  
		m_nPointCount++;
	}
	~Point()
	{
		m_nPointCount--;
	}
	static void output()
	{
		printf("%d\n", m_nPointCount);
	}
private:
	static int m_nPointCount;
};
void main()
{
	Point pt;
	pt.output();
}

按Ctrl+F7编译无错误,按F7生成EXE程序时报链接错误

error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)

这是因为类的静态成员变量在使用前必须先初始化。

在main()函数前加上int Point::m_nPointCount = 0;

再编译链接无错误,运行程序将输出1。

结论5:类的静态成员变量必须先初始化再使用。

 

结合上面的五个例子,对类的静态成员变量和成员函数作个总结

一。静态成员函数中不能调用非静态成员。

二。非静态成员函数中可以调用静态成员。因为静态成员属于类本身,在类的对象产生之前就已经存在了,所以在非静态成员函数中是可以调用静态成员的。

三。静态成员变量使用前必须先初始化(如int MyClass::m_nNumber = 0;),否则会在linker时出错。

 

再给一个利用类的静态成员变量和函数的例子以加深理解,这个例子建立一个学生类,每个学生类的对象将组成一个双向链表,用一个静态成员变量记录这个双向链表的表头,一个静态成员函数输出这个双向链表。

#include <stdio.h>
#include <string.h>
const int MAX_NAME_SIZE = 30;  

class Student  
{  
public:  
    Student(char *pszName);
    ~Student();
public:
	static void PrintfAllStudents();
private:  
    char    m_name[MAX_NAME_SIZE];  
    Student *next;
	Student *prev;
    static Student *m_head;
};  

Student::Student(char *pszName)
{  
    strcpy(this->m_name, pszName);

	//建立双向链表,新数据从链表头部插入。
    this->next = m_head;
	this->prev = NULL;
	if (m_head != NULL)
		m_head->prev = this;
    m_head = this;  
}  

Student::~Student ()//析构过程就是节点的脱离过程  
{  
	if (this == m_head) //该节点就是头节点。
	{
		m_head = this->next;
	}
	else
	{
		this->prev->next = this->next;
		this->next->prev = this->prev;
	}
}  

void Student::PrintfAllStudents()
{
	for (Student *p = m_head; p != NULL; p = p->next)
		printf("%s\n", p->m_name);
}

Student* Student::m_head = NULL;  

void main()  
{   
	Student studentA("AAA");
	Student studentB("BBB");
	Student studentC("CCC");
	Student studentD("DDD");
	Student student("MoreWindows");
	Student::PrintfAllStudents();
}

程序将输出:



C++派生类--基类构造函数,内嵌对象(子对象)的执行顺序

与派生类构造函数的初始化列表顺序无关,是按如下顺序:

1.基类构造函数(按继承时的顺序)

2.子对象构造函数(按类中声明的顺序)

3.派生类自己的构造函数 

析构时:

与构造时相反

 

测试程序如下:

#include <iostream>
using namespace std;

class A
{
public:
 A(){ cout << "A" << endl; }
 ~A(){ cout << "~A" << endl; }
};

class B
{
public:
 B(){ cout << "B" << endl; }
 ~B(){ cout << "~B" << endl; }
};

class C
{
public:
 C(){ cout << "C" << endl; }
 ~C(){ cout << "~C" << endl; }
};

class D
{
public:
 int d;
 D(){ cout << "D1" << endl; }
 D(int a):d(a){ cout << "D2" << endl; }
 ~D(){ cout << "~D" << endl; }
};

class E:public B, public A
{
public:
 D d;
 C c;
 E():A(),c(),B(),d(){ cout << "E" << endl; }
 ~E(){ cout << "~E" << endl; }
};

class F:public E,public A
{
public:
 C cf;
 D df;
 F():A(),cf(),df(5){  cout << "F" << endl; }
 ~F(){ cout << "~F" << endl; }
};

int main(int argc, _TCHAR* argv[])
{
 F f;
 return 0;
}

a+++b 在编译基础上的讨论

关于讨论代码中的 c = a+++b 这个表达式的面试题一直都在出现。从很直观的角度上来说你可以理解成为以下的两种形式

一个是 c = (a++) + b; 另一种是c = a + (++b);

其实做过编译程序的人可以考虑以下过程,在词法分析阶段要进行的是将输入的文件切成token,并形成token序列,你可以知道

对+进行处理的时候将执行以下的代码,当编译程序读到+的时候,首先期望得到的是 = ,如果是,那么好, 是+=如果不适,那么期望得到的是 + ,如果是,那么得到的是 ++; 以上都不是 则后退返回。可见当编译程序遇到上面的表达式的时候,设当前读入字符是第一个+,那么编译程序期望得到的是=或者+,显然可以得到+,那么构成++,存入token,继续进行。所以上述表达式理解成为c = (a++) + b;是正确的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值