【20090819】C++培训日记-一些基础知识
今天的作业包括:函数实现在类中和类外的差别、const的使用、基类使用虚析构函数、C++风格的强制转换、初始化列表与直接赋值的区别、内存分配
函数实现在类内和类外的区别:
如果在类内实现则默认是内联,如果要在类外实现内联函数,则需要加上inline来修饰。
const的使用:yoyo.is-programmer.com/posts/10611
- 与指针混合使用:const T*或T const *, 当const在*左边时,表示指针所指向的值是常量,不可改变,但指针可以移动。(见下列代码中的ReadValueOnly1和ReadValueOnly2)
- 与指针混合使用:T* const或const (T*) , 当const在*右边时,表示指针是常量,不可移动,但是所指向的内容可以改变。(见下列代码中的ReadAddressOnly)
- 与指针混合使用:当const在*的左右都有时,表示该指针是一个常量,不可移动,同时所指向的内容也是常量,不可改变。(见下列代码中的ReadBothOnly)
- const修饰普通变量:在参数列表时可表示该参数仅作输入,不会对其值进行改变,修饰返回值时可以表示这个返回值不会被改变。
- const修饰成员函数:表示该成员函数不会修改对象的成员变量(但可以修改static成员变量)。(见下列代码中的CTest::ReadOnly())
-
#include <iostream>
-
-
void ReadValueOnly1(const int* p)
-
{
-
std::cout << "const int *" << std::endl;
-
-
std::cout << (*p) << std::endl;
-
-
p++;
-
std::cout << (*p) << std::endl;
-
-
//(*p) ++; // error C3892: “p”: 不能给常量赋值
-
std::cout << (*p) << std::endl;
-
-
std::cout << "====================" << std::endl;
-
}
-
-
void ReadValueOnly2(int const * p)
-
{
-
std::cout << "int const *" << std::endl;
-
-
std::cout << (*p) << std::endl;
-
-
p++;
-
std::cout << (*p) << std::endl;
-
-
//(*p) ++; // error C3892: “p”: 不能给常量赋值
-
std::cout << (*p) << std::endl;
-
-
std::cout << "====================" << std::endl;
-
}
-
-
void ReadAddressOnly1(int * const p)
-
{
-
std::cout << "int * const" << std::endl;
-
-
std::cout << (*p) << std::endl;
-
-
//p++; // error C3892: “p”: 不能给常量赋值
-
std::cout << (*p) << std::endl;
-
-
(*p) ++;
-
std::cout << (*p) << std::endl;
-
-
std::cout << "====================" << std::endl;
-
}
-
-
//void ReadAddressOnly2(const (int *) p)
-
//{
-
// std::cout << (*p) << std::endl;
-
//
-
// p++;
-
// std::cout << (*p) << std::endl;
-
//
-
// (*p) ++;
-
// std::cout << (*p) << std::endl;
-
//}
-
-
void ReadBothOnly(const int * const p)
-
{
-
std::cout << "const int * const" << std::endl;
-
-
std::cout << (*p) << std::endl;
-
-
//p++; // error C3892: “p”: 不能给常量赋值
-
std::cout << (*p) << std::endl;
-
-
//(*p) ++; // error C3892: “p”: 不能给常量赋值
-
std::cout << (*p) << std::endl;
-
-
std::cout << "====================" << std::endl;
-
}
-
-
class CTestConst
-
{
-
public:
-
CTestConst(int _member): member(_member) { };
-
~CTestConst(void) { };
-
-
void Fun(void)
-
{
-
std::cout << "test" << std::endl;
-
};
-
-
void ReadMemberOnly() const
-
{
-
std::cout << "const Member Function" << std::endl;
-
-
std::cout << member << std::endl;
-
-
//member++; // error C2166: 左值指定 const 对象
-
std::cout << member << std::endl;
-
-
std::cout << s_var << std::endl;
-
-
s_var++;
-
std::cout << s_var << std::endl;
-
-
std::cout << "====================" << std::endl;
-
};
-
-
private:
-
int member;
-
static int s_var;
-
};
-
int CTestConst::s_var = 30;
-
-
void main(void)
-
{
-
int* p = new int[10];
-
for(int i=0; i<10; i++)
-
{
-
p[i] = i+1;
-
}
-
-
ReadValueOnly1(p);
-
ReadValueOnly2(p);
-
-
ReadAddressOnly1(p);
-
//ReadAddressOnly2(p);
-
-
ReadBothOnly(p);
-
-
CTestConst test(1989);
-
test.Fun();
-
test.ReadMemberOnly();
-
-
const CTestConst constTest = test;
-
//constTest.Fun(); // 不能将“this”指针从“const CTestConst”转换为“CTestConst &”
-
constTest.ReadMemberOnly();
-
-
system("pause");
-
}
基类要使用虚析构函数:
没有使用虚析构函数的类见CBase,它对应的子类是CDerived,可以看到在用CBase指针指向CDerived对象,要删除该指针所指向内容的时候,并不会调用子类的析构函数,而使用CDerived指针指向CDerived对象时顺序是正常的;
而使用了虚析构函数的类CBaseVirtual,对应子类CDerivedVirtual,不论指向子类对象的是父类指针还是子类指针,删除该指针所指向内容的时候都会正常调用子类的析构函数和父类的析构函数。
-
#include <iostream>
-
-
class CBase
-
{
-
public:
-
CBase()
-
{
-
std::cout << "CBase()" << std::endl;
-
};
-
~CBase()
-
{
-
std::cout << "~CBase()" << std::endl;
-
};
-
};
-
-
class CDerived: public CBase
-
{
-
public:
-
CDerived()
-
{
-
std::cout << "CDerived()" << std::endl;
-
};
-
~CDerived()
-
{
-
std::cout << "~CDerived()" << std::endl;
-
};
-
};
-
-
class CBaseVirtual
-
{
-
public:
-
CBaseVirtual()
-
{
-
std::cout << "CBaseVirtual()" << std::endl;
-
};
-
virtual ~CBaseVirtual()
-
{
-
std::cout << "~CBaseVirtual()" << std::endl;
-
};
-
};
-
-
class CDerivedVirtual: public CBaseVirtual
-
{
-
public:
-
CDerivedVirtual()
-
{
-
std::cout << "CDerivedVirtual()" << std::endl;
-
};
-
~CDerivedVirtual()
-
{
-
std::cout << "~CDerivedVirtual()" << std::endl;
-
};
-
};
-
-
void main(void)
-
{
-
CBase* pBase = new CDerived;
-
delete pBase;
-
std::cout << "################################" << std::endl;
-
-
CDerived* pDerived = new CDerived;
-
delete pDerived;
-
std::cout << "################################" << std::endl;
-
-
CBaseVirtual* pBaseVirtual = new CDerivedVirtual;
-
delete pBaseVirtual;
-
std::cout << "################################" << std::endl;
-
-
CDerivedVirtual* pDerivedVirtual = new CDerivedVirtual;
-
delete pDerivedVirtual;
-
std::cout << "################################" << std::endl;
-
-
getchar();
-
}
C++风格的强制转换:
- static_cast:在编译时转换
用法:static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。-
#include <iostream>
-
-
class CBase
-
{
-
public:
-
CBase(void)
-
{
-
};
-
};
-
-
class CDerived: public CBase
-
{
-
};
-
-
class CDerivedSecond: public CBase
-
{
-
-
};
-
-
/*
-
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
-
*/
-
void main(void)
-
{
-
/*
-
用法一:用于类层次结构中基类和子类之间指针或引用的转换。
-
*/
-
{
-
CBase objBase;
-
CDerived objDerived;
-
-
CBase objBaseByCast = static_cast<CBase>(objDerived);
-
// CDerived objDerivedByCast = static_cast<CDerived>(objBase); // error C2440: “static_cast”: 无法从“CBase”转换为“CDerived”
-
-
// 进行上行转换(把子类的指针或引用转换成基类表示)是安全的
-
CBase& rBase = static_cast<CBase&>(objDerived);
-
CBase* pBase = static_cast<CBase*>(&objDerived);
-
-
// 进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的
-
CDerived& rDerived = static_cast<CDerived&>(objBase);
-
CDerived* pDerived = static_cast<CDerived*>(&objBase);
-
}
-
-
/*
-
用法二:用于基本数据类型之间的转换。这种转换的安全性也要开发人员来保证。
-
*/
-
{
-
int i = 30;
-
char ch = static_cast<char>(i);
-
-
std::cout << ch << std::endl;
-
}
-
-
/*
-
用法三:把空指针转换成目标类型的空指针。
-
*/
-
{
-
CBase* pBase = NULL;
-
void* pVoid = static_cast<void*> (pBase);
-
-
int* pInt = static_cast<int*> (pVoid);
-
}
-
-
/*
-
用法四:把任何类型的表达式转换成void类型。
-
*/
-
{
-
static_cast<void>(3.14159f);
-
}
-
-
/*
-
错误用法:指向类型无关
-
*/
-
{
-
float* pFloat = new float(3.14f);
-
//int* pInt = static_cast<int*> (pFloat); // 无法从“float *”转换为“int *”
-
}
-
-
/*
-
错误用法:虽然没有报错,但是没有意义,创建垃圾指针
-
*/
-
{
-
int* pInt = new int(3);
-
int* pRubbish = static_cast<int*> (pInt);
-
}
-
-
/*
-
错误用法:交叉转换
-
*/
-
try
-
{
-
// CDerivedSecond objDerivedSecond;
-
-
// CDerived objDerived = static_cast<CDerived> (objDerivedSecond); // error C2440: “static_cast”: 无法从“CDerivedSecond”转换为“CDerived”
-
// CDerived* pDerived = static_cast<CDerived*> (&objDerivedSecond); // error C2440: “static_cast”: 无法从“CDerivedSecond *__w64 ”转换为“CDerived *”
-
// CDerived& rDerived = static_cast<CDerived&> (objDerivedSecond); // error C2440: “static_cast”: 无法从“CDerivedSecond”转换为“CDerived &”
-
-
// std::cout << pDerived << " " << &objDerivedSecond << std::endl;
-
}
-
catch(std::bad_cast)
-
{
-
std::cout << "Catch Bad Cast CRASH at LINE " << __LINE__ << std::endl;
-
}
-
-
system("pause");
-
}
-
- dynamic_cast:在运行时转换
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
-
#include <iostream>
-
-
/*
-
* 基类必须有virtual函数,否则dynamic_cast时会编译出错:
-
* error C2683: “dynamic_cast”:“CBase”不是多态类型
-
*/
-
class CBase
-
{
-
public:
-
virtual void foo() { };
-
};
-
-
class CDerived: public CBase
-
{
-
-
};
-
-
class CDerivedSecond: public CBase
-
{
-
-
};
-
-
void main(void)
-
{
-
CBase objBase;
-
CDerived objDerived;
-
-
CBase* pBase;
-
CDerived* pDerived;
-
-
try
-
{
-
pBase = dynamic_cast<CBase*> (&objDerived);
-
pDerived = dynamic_cast<CDerived*> (&objBase);
-
-
CBase& rBase = dynamic_cast<CBase&> (objDerived);
-
CDerived& rDerived = dynamic_cast<CDerived&> (objBase);
-
}
-
catch (std::bad_cast)
-
{
-
std::cout << "Catch Bad Cast CRASH at LINE " << __LINE__ << std::endl;
-
}
-
-
std::cout << pBase << " " << &objDerived << std::endl;
-
std::cout << pDerived << " " << &objBase << std::endl; // 空指针
-
-
/*
-
dynamic_cast 还支持交叉转换(cross cast)
-
*/
-
CDerivedSecond objDerivedSecond;
-
-
try
-
{
-
pDerived = dynamic_cast<CDerived*> (&objDerivedSecond);
-
CDerived& rDerived = dynamic_cast<CDerived&> (objDerivedSecond);
-
}
-
catch(std::bad_cast)
-
{
-
std::cout << "Catch Bad Cast CRASH at LINE " << __LINE__ << std::endl;
-
}
-
-
std::cout << pDerived << " " << &objDerivedSecond << std::endl; // 空指针
-
-
system("pause");
-
}
-
- const_cast
用法:const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
-
#include <iostream>
-
-
/*
-
const_cast 可以:
-
将常量指针转换为非常量指针;
-
将常量引用转换为非常量引用;
-
将常量对象转换为非常量对象。
-
*/
-
void main(void)
-
{
-
const int* pInt1 = new int(5);
-
// *pInt1 = 3; // error C3892: “pInt1”: 不能给常量赋值
-
std::cout << *pInt1 << std::endl;
-
-
int* pInt2 = const_cast<int*>(pInt1);
-
std::cout << *pInt2 << std::endl;
-
-
*pInt2 = 2;
-
std::cout << *pInt2 << std::endl;
-
-
system("pause");
-
}
-
- reinterpret_cast
用法:reinpreter_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。-
#include <iostream>
-
-
class A
-
{
-
-
};
-
-
class B
-
{
-
-
};
-
-
/*
-
reinterpret_cast
-
该函数将一个类型的指针转换为另一个类型的指针。
-
这种转换不用修改指针变量值存放格式(不改变指针变量值),只需在编译时重新解释指针的类型就可做到。
-
reinterpret_cast 可以将指针值转换为一个整型数,但不能用于非指针类型的转换。
-
*/
-
void main(void)
-
{
-
// 基本类型指针的类型转换
-
double d = 3.14;
-
int* pI = reinterpret_cast<int*> (&d); // 相当于 (int*) (&d)
-
std::cout << (*pI) << std::endl;
-
-
// 不相关的类的指针的类型转换
-
A* a = new A;
-
B* b = reinterpret_cast<B*> (a);
-
std::cout << &b << " " << &a << std::endl;
-
-
// 指针转换为整数
-
long lAddress = reinterpret_cast<long> (&d);
-
std::cout << lAddress << std::endl;
-
-
system("pause");
-
}
-
总结:
static_cast用于内建数据类型之间的转型。
reinterpret_cast用于对变量重新进行二进制解释,因此它的转型结果是编译器实现相关(implementation-dependent)的,不是可移植的,一般在实践中,只有迫不得已,才使用这种转型。例如直接将整数常量转型为某个自定义的struct类型,驱动程序的编写中常常使用这种技术。
dynamic_cast用于在继承体系中进行安全的向下转型(downcast),也就是从基类指针/引用向派生类指针/引用的转型。
const_cast用于取消变量的常量性修饰。
以及其他显示转换:
- itf_cast:例子貌似都是用COM的IHTML什么来写的……看不懂 = = 先放着···
- stream_cast
-
#include <iostream>
-
#include <sstream>
-
#include <string>
-
-
/************************************************************************/
-
/* 如果已经定义了输入和输出
-
/* 准确地来说,
-
/* 源类型定义了输出(<<),目标类型定义了输入(>>)
-
/* 就可以使用stream_cast进行转换。
-
/*
-
/* std::stringstream 属于 <sstream>
-
/************************************************************************/
-
template<typename T1, typename T2>
-
T1 stream_cast(const T2& t2)
-
{
-
std::stringstream str;
-
str << t2;
-
-
T1 t1;
-
str >> t1;
-
-
if(!str)
-
{
-
throw std::bad_cast();
-
}
-
-
return t1;
-
}
-
-
void main(void)
-
{
-
std::string s("0.5");
-
double d = stream_cast<double>(s);
-
-
std::cout << d << std::endl;
-
-
getchar();
-
}
-
- safe_cast
-
#include <iostream>
-
-
/************************************************************************/
-
/* 如果我们希望(T2)(T1)t2 == t2,即无损失的转换,可以使用safe_cast
-
/************************************************************************/
-
template<typename T1, typename T2>
-
T1 safe_cast(const T2& t2)
-
{
-
if((T2)(T1)t2 != t2)
-
{
-
throw std::bad_cast();
-
}
-
return (T1)t2;
-
}
-
-
void main(void)
-
{
-
try
-
{
-
int i = 5;
-
double c = safe_cast<double> (i);
-
-
double d = 2.4;
-
float f = safe_cast<float> (d);
-
}
-
catch (std::bad_cast)
-
{
-
std::cout << "Bad Cast at " << __LINE__ << std::endl;
-
}
-
-
getchar();
-
}
-
初始化列表与构造函数内部直接赋值:http://yoyo.is-programmer.com/posts/10610
其他待继续笔记……
- 无匹配