c++一些不常见的东西

1.new 在现有的堆上再次分配

char  *cp = new char[100],ch[50];
int *p = new(cp) int ;
*p=58;
delete p;
cp = cp+sizeof(int);
delete cp;//错误,不能释放两次

int *p = new(ch) int ; //利用现有栈空间重新分配

delete *p ; //错误,不能释放栈空间

2.一些喜欢出错的运算符重载(括号、用户自定义)

#include <iostream>
using namespace std;

class test{  
	friend ostream & operator << (const ostream &o,const test &t);  
};  
ostream& operator << (ostream &o,const test &t){  
	cout <<"test"<<endl;  
	return o ;  
}  
class Time{  
public:
	int operator()()    {  //小括号重载 版本0 注意和下面用户自定义转换的区别  
		cout <<"bracket"<<endl;  
		return h*3600 + m*60 + s;  
	}  
	operator test() {  //用户自定义转换 1.用户定义的转换不能指定返回类型 2.“Time::operator test”: 必须返回一个值
		cout <<"cast test"<<endl;  
		static test t;  
		//return t; //返回class test的一个引用  
	}  
	void operator()(int h, int m, int s)    {   //小括号重载 版本3  
		this->h = h;  
		this->m = m;  
		this->s = s;  
	}  
private:
	int h;
	int m;
	int s ;
}  ;
int main(int argc,char**argv){  
	Time t;  
	t(1,1,1) ; //小括号重载 ,并带有三个参数  
	cout<<t()<<endl;//小括号重载 版本0  
	cout<< test (t) ;  
	return 0;  
}  

1.用户定义的转换不能指定返回类型
2.“Time::operator test”: 必须返回一个值


3.const 修饰函数

  const   char*   const   foo(char   const   *   const   str)   const       
  第一个const表示返回类型为const,也就是不能把此函数的返回值当作左值来使用。   
  第二个const表示指针的不可变性,但在这是可以省略,因为返类型已经是const。       
  第三个cosnt表示str的常量性,也就其内容是不能改变,可以写在其前面的char的前面。       
  第四个cosnt表示str的指针的常量性,也就是此指针不能指向别的地址。      
  第五个cosnt表示此函数的常量性(前提是类的成员函数),不能修改所在类的数据成员。  

更详细的用法,参考:http://blog.csdn.net/Eric_Jo/article/details/4138548

4.采用const_cast 进行转换

用法:const_cast <type_id>  (expression) 
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
·             常量指针被转化成非常量指针,并且仍然指向原来的对象;
·             常量引用被转换成非常量引用,并且仍然指向原来的对象;
·             常量对象被转换成非常量对象。

下面还是用代码来说明问题

代码一:

	const int m = 15;
	const int *const_p = &m;
	int *p = const_cast<int*> (const_p) ;
	*p = 18;
	cout << *p<<*const_p<<m; ;
输出结果: 181815

代码二:

	int m = 15;
	const int *const_p = &m;
	int *p = const_cast<int*> (const_p) ;
	*p = 18;
	cout << *p<<*const_p<<m; ;
输出结果: 181818

代码三:

	const int m = 15;
        const int &mconst = m;
	int &mnc =const_cast<int&>(mconst ); //或 int &mnc =const_cast<int&>(m);
	mnc = 80;
	cout << mnc <<mconst <<m;

输出结果:808015

这真是一件奇怪的事情,但是这是件好事:说明C++里是const,就是const,外界千变万变,我就不变。不然真的会乱套了,const也没有存在的意义了。

IBM的C++指南称呼“*modifier = 7;”为“未定义行为(Undefined Behavior)”。所谓未定义,是说这个语句在标准C++中没有明确的规定,由编译器来决定如何处理。
位运算的左移操作也可算一种未定义行为,因为我们不确定是逻辑左移,还是算数左移。
再比如下边的语句:v[i] = i++; 也是一种未定义行为,因为我们不知道是先做自增,还是先用来找数组中的位置。
对于未定义行为,我们所能做的所要做的就是避免出现这样的语句。对于const数据我们更要这样保证:绝对不对const数据进行重新赋值。

更详细的内容参考: http://www.cnblogs.com/ider/archive/2011/07/22/cpp_cast_operator_part2.html


5.不可以拷贝的对象

c++一些不常见的东西_第1张图片

C++中不可拷贝的对象一般为IO对象,不可拷贝的原因:io对象操作各种不同的输入输出设备的句柄,如果允许复制,会出现两个不同的对象操作同一个设备句柄,如果其中一个析构了,该设备被关闭,但另一个对象仍使用该句柄,会导致不可预测的运行期错误,是危险的。因此io对象不允许复制。

不可复制的实现:

在C++中,类的拷贝主要是通过拷贝构造函数和赋值函数,再者就是为拷贝专门实现的成员方法。由于拷贝构造函数和赋值函数在用户为提供的情况下是由C++编译器自动生成的,而且是public成员,因此默认的C++类都有拷贝功能。因此,可以显示的将构造函数和赋值函数设为私有成员,

在此留一下问题: 友员friend  是可以访问私友成员,如果让友员也不访问部分私友成员呢???


6.RTTI(Run-Time Type Identification)

先看看下面一段代码:

#include <iostream>
using namespace std;

class A{
	int m[15];
public:
	virtual void prinA(){cout <<"this is A"<<endl;} 
};
class B: public A{ //若换成虚继承,会有什么情况?
public:
	virtual void prinB(){cout <<"this is B"<<endl;} 
};

void fun_1(A* pa){
	B* pb = dynamic_cast<B*>(pa);
	if(pb==NULL){
		pa->prinA();
	}else{
		pb->prinB();
	}
}
void fun_2(A* pa){
	B*pb=NULL;
	if(typeid(*pa)== typeid(B) ){
		 pb= static_cast<B*>(pa) ;
		pb->prinB();
	}else{
		pa->prinA();
	}
}

int main(){
	//cout <<sizeof(A)<<sizeof(B);//6464
	A * pa =new B ;
	cout <<typeid(pa).name()<<endl;
	cout <<typeid(*pa).name()<<endl;
	fun_1(pa);
	fun_2(pa);
	A* paa = new A ;
	fun_1(paa);
	fun_2(paa);
	return 0;
}

先不用看运行结果,可以先试着做做,看看与运行结果是否一样! 微笑

运行结果:

class A *
class B
this is B
this is B
this is A
this is A
Press any key to continue . . .

至于细仔的说明,这里就不弄出来了,给一个参考的链接吧!

更多细节参考:http://www.cnblogs.com/zhyg6516/archive/2011/03/07/1971898.html


7.typeid

在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型,即允许“用指向基类的指针或引用来操作对象”的程序能够获取到“这些指针或引用所指对象”的实际派生类型。在C++中,为了支持RTTI提供了两个操作符:dynamic_cast和typeid。
    dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型,与之相对应的还有一个非安全的转换操作符static_cast,因为这不是本文的讨论重点,所以这里不再详述,感兴趣的可以自行查阅资料。下面就开始今天我们的话题:typeid。
    
    typeid是C++的关键字之一,等同于sizeof这类的操作符。typeid操作符的返回结果是名为type_info的标准库类型的常量对象的引用(在头文件typeinfo中定义,稍后我们看一下vs和gcc库里面的源码),它的表达式有下图两种形式。


    如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。
    ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作(在之后的章节中我会来分析type_info类文件的源码):
 t1 == t2  如果两个对象t1和t2类型相同,则返回true;否则返回false
 t1 != t2  如果两个对象t1和t2类型不同,则返回true;否则返回false
 t.name()  返回类型的C-style字符串,类型名字用系统相关的方法产生
 t1.before(t2)  返回指出t1是否出现在t2之前的bool值
    type_info类提供了public虚 析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info的 友元)。type_info的name成员函数返回C-style的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。

8.箭头重载

1.重载箭头操作符必须返回指向类类型的指针,
2.或者返回定义了自己的箭头操作符的类类型对象

#include <iostream>
using namespace std;

class A{
public:
	void action(){
		cout << "Action in class A!" << endl;
	}
};

class B{
	A a;
public:
	A* operator->(){
		return &a;
	}
	void action(){
		cout << "Action in class B!" << endl;
	}
	void printB(){
		cout << "I am B" <<endl;
	}
};

class C{
	B b;
public:
	B operator->(){
		return b;
	}
	void action(){
		cout << "Action in class C!" << endl;
	}
};

int main(int argc, char *argv[]){

	B b;

	//b->printB();	//error C2039: “printB”: 不是“A”的成员
	b->action() ;	//正确,调用 A::action()

	C* pc = new C;
	pc->action();	//正确,调用C::action();

	C c;
	c->action();	//正确,调用C::operator-> ,再调用B::operator->,再调用A::operator->,最后调用A::action()
	
	getchar();

	return 0;
}

9.等继.....










你可能感兴趣的:(c++一些不常见的东西)