欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

复习C++基础知识-----“我的第一本C++”读书笔记2

程序员文章站 2024-01-04 21:27:46
...

抽象一般分为属性抽象和行为抽象两种。前者寻找一类对象共有的属性或者状态变量,后者则寻找这类对象所具有的共同行为特征。在分析新的对象时,应该从属性和行为两个方面进行抽象和概括,提取对象的共有也行。有了抽象,那么就可以提取出来当做接口(虚函数),可以直接变成类的成员属性和成员函数。


如何在子类中调用从父类继承并且已经被重写的函数?--------------------

#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 对象

上一篇:

下一篇: