复习C++基础知识-----“我的第一本C++”读书笔记2
2012-07-03 23:42
471 查看
抽象一般分为属性抽象和行为抽象两种。前者寻找一类对象共有的属性或者状态变量,后者则寻找这类对象所具有的共同行为特征。在分析新的对象时,应该从属性和行为两个方面进行抽象和概括,提取对象的共有也行。有了抽象,那么就可以提取出来当做接口(虚函数),可以直接变成类的成员属性和成员函数。
如何在子类中调用从父类继承并且已经被重写的函数?--------------------
this指针 :
比如在类中
SetValue函数中并没有指明m_nVal成员变量到底属于哪一个对象类似的问题...其实编译器隐藏掉了,应该是this->m_nVal = nVal;当然在使用的时候,可以直接在这个变量前面显示的加上去。
但是,this指针在实际开发中的意义却是,用来返回指向对象本身的指针,以实现对象链式引用,或者避免对同一对象进行赋值操作。例如
指针* :
1)指针加1或者减1,会使指针指向的地址增加或者减少一个对象的数据类型的长度。
2)指针类型的转换
虽然指针类型的转换可能会带来不可预料的麻烦,就行goto语句一样,比如
这种方法比较直接,但是非常粗鲁,因为他允许你在任何类型之间进行转换,另外这种类型的转换方式在程序语句中很难识别,代码阅读者可能会忽略类型转换的语句。
为了克服这些缺点,C++引入了新的类型转换操作符static_cast来代替上面的类型转换
static_cast<类型说明符>(表达式)
static_cast
指针的类型转换-------------------------------------------------------
二级指针的使用** :
一个例子既可以知道
指针在函数中的作用 :
1)在函数的参数中使用指针,在传递大数据的时候可以有效减少函数调用的开销
2)指针作为函数返回值
当函数的返回值是指针时,这个函数就是指针型函数。指针型函数通常用来获取一些指针变量的值。
在类中返回一个指针类型的成员变量,经常会使用到指针作为函数返回值
引用& :
1)引用的本质,就是变量的别名,通俗地讲,就是变量的绰号。对变量的引用进行任何操作,就是对变量本身的操作,就想不管是叫你小名还是叫你的绰号,都是在叫同一个人。
2)在函数参数中使用,传递引用可以直接对这个参数进行修改
三种函数参数和返回值的方法 :
1)
传值 是指直接将实际参数的值复制给形参,完成参数的传递
形式简单自然,便于理解,代码可读性高
2)
传指针 是指将需要传递的数据的指针作为参数进行传递
效率高,可以同时传入传出参数
3)
传引用 将需要传递的数据的引用作为参数进行传递
效率高,可以同时传入传出参数,形式自然
异常处理
异常的使用,会降低程序的性能,但是,如果使用得当,有时也可以提高程序的性能。比如,如果函数的参数是指针,则需要在函数入口处对这个指针的有效性进行检查。
名字空间namespace :
主要在多人同时开发时候使用,避免冲突
自定义类型的使用typedef :
后代前,以后使用byte就相当于使用了unsigned char
宏的使用#define
1)简单的使用
#define MAXSIZE 100
2)宏直接分配数组
3)宏中定义函数
用const保护数据
1)const定义常量
2)根据const实在*的位置判断 :
const在*左边,则表示const修饰的是int,这个指针指向的int变量的值不能修改,而指针本身的值是可变的;
如果const在*的右边,则表示const修饰的是指针,这个指针的值不能在声明后修改,所以在声明这样的指针时必须赋初值,而这个指针所指向的int变量的值是可变的。
3)在函数参数中加入const
表示这只是一个传入参数,在整个函数内部不能被修改。
4)修饰类成员函数
在声明类的成员函数时,如果在末尾加上const修饰,则表示在这个成员函数内不得改变该对象的任何数据。这种模式常用来表示"对象数据制度"的访问模式。
// 提示error : error C2166: 左值指定 const 对象
如何在子类中调用从父类继承并且已经被重写的函数?--------------------
#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 对象
相关文章推荐
- 复习C++基础知识-----“我的第一本C++”读书笔记3
- 复习C++基础知识-----“我的第一本C++”读书笔记1
- 复习C++基础知识-----“我的第一本C++”读书笔记4(终篇)
- 计算机网络-C++基础知识,面试复习题
- [读书笔记]C++基础知识温习:堆栈
- C++基础知识复习--字符串
- 复习C++一些基础知识
- c/c++基础知识读书笔记三 结构化数据
- [读书笔记]C++基础知识温习:重载递增/减运算符
- C++基础知识复习-【1】进制转换系列之16进制转10进制
- C++基础知识复习&总结
- C++基础知识复习--函数重载
- [读书笔记]C++基础知识温习:智能指针
- C++基础知识复习 const关键字
- C++基础知识复习&总结
- Professional C++ 01 A Crash Course in C++ 快速的C++基础知识复习
- [读书笔记]C++基础知识温习:using
- 《C++高级进阶》读书笔记 第一章 C++基础知识
- [读书笔记]C++基础知识温习:预处理指令
- C++基础知识复习与总结(1)---C++内存管理