复习C++基础知识-----“我的第一本C++”读书笔记2
抽象一般分为属性抽象和行为抽象两种。前者寻找一类对象共有的属性或者状态变量,后者则寻找这类对象所具有的共同行为特征。在分析新的对象时,应该从属性和行为两个方面进行抽象和概括,提取对象的共有也行。有了抽象,那么就可以提取出来当做接口(虚函数),可以直接变成类的成员属性和成员函数。
如何在子类中调用从父类继承并且已经被重写的函数?--------------------
#include "stdafx.h"
#include "iostream"
using namespace std;
namespace Zeng
{
class CTest_A
{
public:
CTest_A( int iValue )
{
this->m_iValue = iValue;
}
void print()
{
cout << "CTest_A's m_iValue current value is : " << this->m_iValue << endl;
}
private:
int m_iValue;
};
class CTest_B : public CTest_A
{
public:
CTest_B( int iValue ) : CTest_A( iValue )
{
this->m_iValue = iValue;
}
void print()
{
cout << "CTest_B's m_iValue current value is : " << this->m_iValue << endl;
}
private:
int m_iValue;
}; // virtual CTest_A比普通的public CTest_A多了一个指向父类的指针
}
int _tmain(int argc, _TCHAR* argv[])
{
Zeng::CTest_A* CB2 = new Zeng::CTest_B( 8 ); // 构造函数的执行顺序是先父类在子类
CB2->print(); // 此时调用的是父类的print函数,因为指针时指向CTest_A的,如果在CTest_B的print前面加virtual还调用CTest_B的
cout << "class CTest_A size is : " << sizeof( Zeng::CTest_A ) << endl;
cout << "class CTest_B size is : " << sizeof( Zeng::CTest_B ) << endl;
return 0;
}
this指针 :
比如在类中
class Base
{
public:
void SetValue( int nVal )
{
m_nVal = nVal;
}
private:
int m_nVal;
}
SetValue函数中并没有指明m_nVal成员变量到底属于哪一个对象类似的问题...其实编译器隐藏掉了,应该是this->m_nVal = nVal;当然在使用的时候,可以直接在这个变量前面显示的加上去。
但是,this指针在实际开发中的意义却是,用来返回指向对象本身的指针,以实现对象链式引用,或者避免对同一对象进行赋值操作。例如
class Point
{
public:
Point( int x, int y ) : m_nX( x ), m_nY( y )
{};
void operator = (Point& pt)
{
// 判断传递进来而定参数是否是这个对象本身,是,则不进行赋值操作
if( &pt == this )
{
m_nX = pt.m_nX;
m_nY = pt.m_nY;
}
}
// 移动点的位置
Point& Move( int x, int y )
{
m_nX += x;
m_nY += y;
// 返回对象本身,这样可以利用函数返回值进行链式引用
return *this;
}
private:
int m_nX;
int m_nY;
};
Point pt1(2, 4);
Point pt2(0, 0);
// 自己给自己赋值 试试
pt1 = pt1;
// 移动一下,再移动一下 看看什么是返回对象的链式引用------------------------
pt1.Move( 1, 1 ).Move( 2, 4 );
指针* :
1)指针加1或者减1,会使指针指向的地址增加或者减少一个对象的数据类型的长度。
2)指针类型的转换
虽然指针类型的转换可能会带来不可预料的麻烦,就行goto语句一样,比如
int* pInt;
float* pFloat = ( float* )pInt;
这种方法比较直接,但是非常粗鲁,因为他允许你在任何类型之间进行转换,另外这种类型的转换方式在程序语句中很难识别,代码阅读者可能会忽略类型转换的语句。
为了克服这些缺点,C++引入了新的类型转换操作符static_cast来代替上面的类型转换
static_cast<类型说明符>(表达式)
static_cast
指针的类型转换-------------------------------------------------------
#include "stdafx.h"
#include "iostream"
using namespace std;
namespace Zeng
{
class CTest_A
{
public:
CTest_A()
{}
virtual void Print()
{
cout << "this's CTest_A's Print :" << endl;
}
};
class CTest_B : public CTest_A
{
public:
CTest_B()
{}
void Print()
{
cout << "this's CTest_B's Print :" << endl;
}
};
class CTest_C
{
public:
CTest_C()
{}
void Print()
{
cout << "this's CTest_C's Print :" << endl;
}
};
class CTest_D
{
public:
CTest_D() : m_iNum(14)
{
}
void ConstPrint() const
{
cout << "ConstPrint print CTest_D m_iNum current value is :" << m_iNum << endl;
}
void Print()
{
cout << "Print print CTest_D m_iNum current value is :" << m_iNum << endl;
}
int m_iNum;
}; // 用来测试const_cast转换操作符
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "CTest_B convert to CTest_A :" << endl;
Zeng::CTest_B* B = new Zeng::CTest_B();
Zeng::CTest_A* A = static_cast< Zeng::CTest_A* >( B );
A->Print();
cout << "\n";
cout << "CTest_A convert to CTest_B :" << endl;
Zeng::CTest_A* A2 = new Zeng::CTest_A();
Zeng::CTest_B* B2 = static_cast< Zeng::CTest_B* >( A2 );
B2->Print();
/*
cout << "\n";
cout << "CTest_B convert to CTest_C :" << endl;
Zeng::CTest_B* B3 = new Zeng::CTest_B();
Zeng::CTest_C* C = static_cast< Zeng::CTest_C* >( B3 );
// the CTest_B has nothing to do with CTest_C, so this convert been an error !
B2->Print();
*/
cout << "\n";
cout << "CTest_B convert to CTest_C :" << endl;
Zeng::CTest_B* B3 = new Zeng::CTest_B();
Zeng::CTest_C* C = reinterpret_cast< Zeng::CTest_C* >( B3 );
// reinterpret_cast should be convert CTest_B to CTest_C,
C->Print();
cout << "\n";
cout << "CTest_A dynamic_cast to CTest_B :" << endl;
Zeng::CTest_A* A4 = new Zeng::CTest_A();
Zeng::CTest_B* B4 = dynamic_cast< Zeng::CTest_B* >( A4 ); // dynamic_cast要求CTest_B必须要有虚函数
// B4->Print(); // 就算是有虚函数,dynamic_cast在处理向下转行的时候,得到的也是NULL
cout << "\n";
cout << "CTest_B dynamic_cast to CTest_A :" << endl;
Zeng::CTest_B* A5 = new Zeng::CTest_B();
Zeng::CTest_A* B5 = dynamic_cast< Zeng::CTest_A* >( A5 ); // dynamic_cast要求CTest_B必须要有虚函数
B5->Print(); // 就算是有虚函数,dynamic_cast在处理向上转行的时候,得到的是和static_cast一样的结果
int iNum = 14;
int* pINum = &iNum;
char* pCTest = "a";
/* pCTest = reinterpret_cast< int* >( pINum );*/
cout << "pINum point value is : " << *pINum << endl;
// reinterpret_cast可以在任意指针中转换,即使这两者之间没什么关系
pINum = reinterpret_cast< int* >( pCTest );
cout << "pINum point value is : " << *pINum << endl;
cout << "\n";
const int iNum2 = 14;
const int* piNum = &iNum2;
int* piValue = const_cast< int* >( piNum );
*piValue = 70;
cout << "use operator const_cast current *piValue value is : " << *piValue << endl;
cout << "use operator const_cast current *piNum value is : " << *piNum << endl;
cout << "use operator const_cast current iNum value is : " << iNum2 << endl;
cout << "\n";
const Zeng::CTest_D CD;
// CD.m_iNum = 70; // error C3892: “CD”: 不能给常量赋值
const Zeng::CTest_D* pCD = &CD;
Zeng::CTest_D* pCD2 = const_cast< Zeng::CTest_D * >( pCD );
pCD2->m_iNum = 70;
cout << "use const_cast operator as object :" << endl;
cout << "pCD2 is not's const point, this point m_iNum value is" << endl;
pCD2->Print();
cout << "CD is a class object, this object m_iNum value is" << endl;
CD.ConstPrint(); // const 对象只能访问class里面带有const的函数
cout << "pCD is a const point, this point m_iNum value is" << endl;
pCD->ConstPrint();
return 0;
}
二级指针的使用** :
一个例子既可以知道
char* arrMouth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
char** pMouth = arrMouth;
int nIndex;
cout << "请输入月份对应的数字 : " << endl;
cin >> nIndex;
// 之所以是char*是因为每一个月份都是字符串类型的
char* pCurMonth = *( pMouth + ( nIndex - 1 ) );
cout << "对应的月份是 : " << pCurMonth << endl;
cout << "对应的月份是 : " << *pCurMonth << endl;
指针在函数中的作用 :
1)在函数的参数中使用指针,在传递大数据的时候可以有效减少函数调用的开销
void SumArray( const int* pArray, int nArrayCount, int* nSum )
{
*nSum = 0;
// 遍历整个数组
for (int i = 0; i < nArrayCount; i++)
{
*nSum += *pArray;
pArray++;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "指针作为函数参数:" << endl;
int nArraySum;
int iArray[5] = { 1, 2, 3, 4, 5};
SumArray( iArray, 5, &nArraySum );
cout << "运算的和为:" << nArraySum << endl;
return 0;
}
2)指针作为函数返回值
当函数的返回值是指针时,这个函数就是指针型函数。指针型函数通常用来获取一些指针变量的值。
在类中返回一个指针类型的成员变量,经常会使用到指针作为函数返回值
char* GetName()
{
return m_pName;
}
引用& :
1)引用的本质,就是变量的别名,通俗地讲,就是变量的绰号。对变量的引用进行任何操作,就是对变量本身的操作,就想不管是叫你小名还是叫你的绰号,都是在叫同一个人。
cout << "use reference:" << endl;
int nIntValue = 99999;
int& rIntValue = nIntValue;
cout << "rIntValue:" << rIntValue << endl;
cout << "rIntValue memory address:" << &rIntValue << endl;
cout << "nIntValue:" << nIntValue << endl;
cout << "nIntValue memory address:" << &nIntValue << endl;
rIntValue = 1;
cout << "modify rIntValue after:" << rIntValue << endl;
cout << "rIntValue memory address:" << &rIntValue << endl;
cout << "current nIntValue:" << nIntValue << endl;
cout << "nIntValue memory address:" << &nIntValue << endl;
nIntValue = 8888;
cout << "modify nIntValue after:" << nIntValue << endl;
cout << "nIntValue memory address:" << &nIntValue << endl;
cout << "current rIntValue:" << rIntValue << endl;
cout << "rIntValue memory address:" << &rIntValue << endl;
2)在函数参数中使用,传递引用可以直接对这个参数进行修改
void Increase( int& nVal )
{
nVal += 1;
}
三种函数参数和返回值的方法 :
1)
传值 是指直接将实际参数的值复制给形参,完成参数的传递
形式简单自然,便于理解,代码可读性高
2)
传指针 是指将需要传递的数据的指针作为参数进行传递
效率高,可以同时传入传出参数
3)
传引用 将需要传递的数据的引用作为参数进行传递
效率高,可以同时传入传出参数,形式自然
异常处理
异常的使用,会降低程序的性能,但是,如果使用得当,有时也可以提高程序的性能。比如,如果函数的参数是指针,则需要在函数入口处对这个指针的有效性进行检查。
double Divede( int a, int b )
{
if ( 0 == b )
{
throw "除数不能为 0 ";
}
return ( double )a / b;
}
cout << "use exception deal : " << endl;
try
{
Divede( 2, 0 );
cout << "throw a exception : " << endl;
}
catch ( char* pMsg )
{
cout << "catch a exception : " << pMsg << endl;
}
catch (...)
{
cout << "catch a exception : " << endl;
}
cout << "is this appcation exit ? : " << endl;
名字空间namespace :
主要在多人同时开发时候使用,避免冲突
namespace Zeng
{
class CTest
{
public:
void Print()
{
cout << "this is namespace Zeng's Print" << endl;
}
};
}
cout << "use namespace Zeng ? : " << endl;
Zeng::CTest CZengText;
CZengText.Print();
自定义类型的使用typedef :
后代前,以后使用byte就相当于使用了unsigned char
typedef unsigned char byte;
宏的使用#define
1)简单的使用
#define MAXSIZE 100
2)宏直接分配数组
#define myInitArray(ArrayName, ArraySize, InitValue) byte ArrayName[ArraySize + 1] = InitValue
3)宏中定义函数
#ifndef SAFE_DELETE
#define SAFE_DELETE( p ) { \
if( NULL != p ) \
{ \
delete p; p = NULL; \
cout << "safe delete..." << endl; \
} }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY( p ) { \
if( NULL != p ) \
{ \
delete[] p; p = NULL; \
cout << "safe delete[]..." << endl; \
} }
#endif
Zeng::CTest* CZengText2 = new Zeng::CTest;
SAFE_DELETE( CZengText2 );
用const保护数据
1)const定义常量
const int number = 1; // 声明一个整形常量并复制为1
const int* pNumber; // 声明一个常量型指针,指针所指向的变量的值不能改变,也就是一个常量
int const* pNumber; // 声明一个常量整型指针,意义同上
int* const pNumber = &number; // 声明一个整型常量指针,指针不能修改
const int* const pNumber = &number;// 声明一个常量整型常量指针,指针和指针所指向的变量值都不能改变
const int& number = number; // 声明一个常量整型引用
2)根据const实在*的位置判断 :
const在*左边,则表示const修饰的是int,这个指针指向的int变量的值不能修改,而指针本身的值是可变的;
如果const在*的右边,则表示const修饰的是指针,这个指针的值不能在声明后修改,所以在声明这样的指针时必须赋初值,而这个指针所指向的int变量的值是可变的。
3)在函数参数中加入const
表示这只是一个传入参数,在整个函数内部不能被修改。
4)修饰类成员函数
在声明类的成员函数时,如果在末尾加上const修饰,则表示在这个成员函数内不得改变该对象的任何数据。这种模式常用来表示"对象数据制度"的访问模式。
namespace Zeng
{
class CTest
{
public:
void Print() const
{
m_nValue = 1;
cout << "this is namespace Zeng's Print" << endl;
}
private:
int m_nValue;
};
}
// 提示error : error C2166: 左值指定 const 对象