`
piperzero
  • 浏览: 3470648 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

复习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 对象

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics