【C/C++】C++类基础知识点
2014-03-20 00:56
302 查看
【C/C++】C++类基础知识点
1. 类的概念:
类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为class ,其功能与C语言中的struct类似,不同之处是class可以包含函数,而不像struct只能包含数据元素。
2. 类的三个范围标志:
private :class的private成员,只有同一个class的其他成员或该class的“friend” class可以访问这些成员。
protected :class的protected成员,只有同一个class的其他成员,或该class的“friend” class,或该class的子类(derived classes) 可以访问这些成员。
public :class的public成员,任何可以看到这个class的地方都可以访问这些成员。
类的范围默认为private,而结构体默认为public
3. 在class外定义变量,必须用::范围操作符:
4. 构造函数和析构函数:
为了避免对象为初始化,调用其成员,得到不确定的值。Class可以包含一个构造函数。构造函数 constructor,它可以通过声明一个与class同名的函数来定义。当且仅当要生成一个class的新的实例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该class的一个对象分配内存的时候,这个构造函数将自动被调用。
析构函数Destructor 完成相反的功能。它在objects被从内存中释放的时候被自动调用。释放可能是因为它存在的范围已经结束了(例如,如果object被定义为一个函数内的本地(local)对象变量,而该函数结束了);或者是因为它是一个动态分配的对象,而被使用操作符delete释放了。
析构函数必须与class同名,加水波号tilde (~) 前缀,必须无返回值。
5. 构造函数重载:
像其它函数一样,一个构造函数也可以被多次重载(overload)为同样名字的函数,但有不同的参数类型和个数。记住,编译器会调用与在调用时刻要求的参数类型和个数一样的那个函数。
6. 默认构造函数和复制构造函数:
当我们定义一个class而没有明确定义构造函数的时候,编译器会自动假设两个重载的构造函数 (默认构造函数"default constructor" 和复制构造函数"copy constructor")。
Empty constructor
它是一个没有任何参数的构造函数,被定义为nop (没有语句)。它什么都不做。
它是一个只有一个参数的构造函数,该参数是这个class的一个对象,这个函数的功能是将被传入的对象(object)的所有非静态(non-static)成员变量的值都复制给自身这个object。
这两个默认构造函数(empty construction 和 copy constructor )只有在没有其它构造函数被明确定义的情况下才存在。
7. 操作符重载:
先看一个列子,计算二维向量的和,结果是(3+1,1+2) = (4,3):
以下列表总结了不同的操作符函数是怎样定义声明的 (用操作符替换每个@):
从上表可以看出有两种方法重载一些class操作符:作为成员函数(member function)或作为全域函数(global function)。它们的用法没有区别,但是我要提醒你,如果不是class的成员函数,则不能访问该class的private 或 protected 成员,除非这个全域函数是该class的 friend 。所以,用成员函数的方式会更好一些。
8. 关键字this:
关键字this 通常被用在一个class内部,指正在被执行的该class的对象(object)在内存中的地址。它是一个指针,其值永远是自身object的地址。
它可以被用来检查传入一个对象的成员函数的参数是否是该对象本身。
它还经常被用在成员函数operator= 中,用来返回对象的指针(避免使用临时对象)。
9. 静态成员:
静态变量:静态数据成员也被称作类变量"class variables",因为它们的内容不依赖于某个对象,对同一个class的所有object具有相同的值。静态成员与全域变量(global variable)具有相同的属性,但它享有类(class)的范围。
静态函数:static函数是全域函数(global functions),但是像一个指定class的对象成员一样被调用。它们只能够引用static 数据,永远不能引用class的非静态(nonstatic)成员。它们也不能够使用关键字this,因为this实际引用了一个对象指针,但这些 static函数却不是任何object的成员,而是class的直接成员。
10. 友元函数(Friend functions):
在类中,用在外部函数申明前加关键字friend,可以允许这个外部访问本类protected 和 private 的成员。
11. 友元类 (Friend classes):
就像我们可以定义一个friend 函数,我们也可以定义一个class是另一个的friend,以便允许第二个class访问第一个class的 protected 和 private 成员。
12. 基类/派生类 父类/子类:
要定义一个类的子类,我们必须在子类的声明中使用冒号(colon)操作符: ,如下所示:
class derived_class_name: public base_class_name;
这里derived_class_name 为子类(derived class)名称,base_class_name 为基类(base class)名称。
当定义一个子类的时候,基类的protected 成员可以被子类的其它成员所使用,然而private 成员就不可以。
下表按照谁能访问总结了不同访问权限类型:
13. 子类从父类继承了什么?
理论上说,子类(drived class)继承了基类(base class)的所有成员,除了:
构造函数Constructor 和析构函数destructor
operator=() 成员
friends
虽然基类的构造函数和析构函数没有被继承,但是当一个子类的object被生成或销毁的时候,其基类的默认构造函数 (即,没有任何参数的构造函数)和析构函数总是被自动调用的。
如果基类没有默认构造函数,或你希望当子类生成新的object时,基类的某个重载的构造函数被调用,你需要在子类的每一个构造函数的定义中指定它。
子类指定了调用父类的构造/析构函数,就按照指定的调用,如果没有指定,就调用父类的默认构造/析构函数。
14. 多重继承:
就是一个子类有多个父类的情况,定义时各父类用逗号’ , ‘分开。
15. 基类的指针:
继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)的指针是type-compatible的。
在这个例子中,在主函数 main 中定义了两个指向class CPolygon的对象的指针,即 *ppoly1 和 *ppoly2。 它们被赋值为rect 和 trgl的地址,因为rect 和 trgl是CPolygon 的子类的对象,因此这种赋值是有效的。
使用*ppoly1 和 *ppoly2 取代rect 和trgl 的唯一限制是*ppoly1 和 *ppoly2 是CPolygon* 类型的,因此我们只能够引用CRectangle 和 CTriangle 从基类CPolygon中继承的成员。正是由于这个原因,我们不能够使用*ppoly1 和 *ppoly2 来调用成员函数 area(),而只能使用rect 和 trgl来调用这个函数。
16. 虚拟成员(Virtual members):
如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字virtual ,以便可以使用指针对指向相应的对象进行操作。
定义了virtual member之后,ppoly1->area()、ppoly2->area()这种形式是允许的。因为area() 被定义为virtual 是因为它后来在子类中被细化了。
17. 抽象基类(Abstract base classes):
在基类中,不对virtual member进行定义,而简单的在函数声明后面写 =0 (等于0),这种函数称为纯虚函数,包含纯虚函数的类称作抽象基类。
抽象基类的最大不同是它不能够有实例(对象),但我们可以定义指向它的指针。因此:
CPolygon poly; /* 是不合法的 */
不合法是因为该类包含的纯虚拟函数(pure virtual function) 是没有被实现的,而又不可能生成一个不包含它的所有成员定义的对象。然而,因为这个函数在其子类中被完整的定义了,所以生成一个指向其子类的对象的指针是完全合法的。
抽象类和虚拟成员赋予了C++ 多态(polymorphic)的特征,使得面向对象的编程object-oriented programming成为一个有用的工具。
何为多态,看下例,不必考虑具体是哪个子类,而把area()打印出来:
1. 类的概念:
类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为class ,其功能与C语言中的struct类似,不同之处是class可以包含函数,而不像struct只能包含数据元素。
2. 类的三个范围标志:
private :class的private成员,只有同一个class的其他成员或该class的“friend” class可以访问这些成员。
protected :class的protected成员,只有同一个class的其他成员,或该class的“friend” class,或该class的子类(derived classes) 可以访问这些成员。
public :class的public成员,任何可以看到这个class的地方都可以访问这些成员。
类的范围默认为private,而结构体默认为public
3. 在class外定义变量,必须用::范围操作符:
class CRectangle { int x, y; public: void set_values (int,int); int area (void) {return (x*y);} }; void CRectangle::set_values (int a, int b) { x = a; y = b; }
4. 构造函数和析构函数:
为了避免对象为初始化,调用其成员,得到不确定的值。Class可以包含一个构造函数。构造函数 constructor,它可以通过声明一个与class同名的函数来定义。当且仅当要生成一个class的新的实例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该class的一个对象分配内存的时候,这个构造函数将自动被调用。
析构函数Destructor 完成相反的功能。它在objects被从内存中释放的时候被自动调用。释放可能是因为它存在的范围已经结束了(例如,如果object被定义为一个函数内的本地(local)对象变量,而该函数结束了);或者是因为它是一个动态分配的对象,而被使用操作符delete释放了。
析构函数必须与class同名,加水波号tilde (~) 前缀,必须无返回值。
class CRectangle { int *width, *height; public: CRectangle (int,int); ~CRectangle (); int area (void) {return (*width * *height);} }; CRectangle::CRectangle (int a, int b) { width = new int; height = new int; *width = a; *height = b; } CRectangle::~CRectangle () { delete width; delete height; }
5. 构造函数重载:
像其它函数一样,一个构造函数也可以被多次重载(overload)为同样名字的函数,但有不同的参数类型和个数。记住,编译器会调用与在调用时刻要求的参数类型和个数一样的那个函数。
6. 默认构造函数和复制构造函数:
当我们定义一个class而没有明确定义构造函数的时候,编译器会自动假设两个重载的构造函数 (默认构造函数"default constructor" 和复制构造函数"copy constructor")。
Empty constructor
它是一个没有任何参数的构造函数,被定义为nop (没有语句)。它什么都不做。
CExample::CExample () { }; Copy constructor
它是一个只有一个参数的构造函数,该参数是这个class的一个对象,这个函数的功能是将被传入的对象(object)的所有非静态(non-static)成员变量的值都复制给自身这个object。
CExample::CExample (const CExample& rv){ a=rv.a; b=rv.b; c=rv.c; }
这两个默认构造函数(empty construction 和 copy constructor )只有在没有其它构造函数被明确定义的情况下才存在。
7. 操作符重载:
先看一个列子,计算二维向量的和,结果是(3+1,1+2) = (4,3):
#include <iostream.h> class CVector { public: int x,y; CVector () {}; /* 空构造函数保证默认构造函数有效, 但不建议这样使用, 建议的使用方法是:CVector ( ) { x=0; y=0; }; */ CVector (int,int); CVector operator + (CVector); }; CVector::CVector (int a, int b) { x = a; y = b; } CVector CVector::operator+ (CVector param) { CVector temp; temp.x = x + param.x; temp.y = y + param.y; return (temp); } int main () { CVector a (3,1); CVector b (1,2); CVector c; c = a + b; /*也可以这样调用c = a.operator+ (b); */ cout << c.x << "," << c.y; return 0; }
以下列表总结了不同的操作符函数是怎样定义声明的 (用操作符替换每个@):
Expression | Operator (@) | Function member | Global function |
@a | + - * & ! ~ ++ -- | A::operator@( ) | operator@(A) |
a@ | ++ -- | A::operator@(int) | operator@(A, int) |
a@b | + - * / % ^ & | < > == != <= >= << >> && || , | A::operator@(B) | operator@(A, B) |
a@b | = += -= *= /= %= ^= &= |= <<= >>= [ ] | A::operator@(B) | - |
a(b, c...) | ( ) | A::operator()(B, C...) | - |
a->b | -> | A::operator->() | - |
Expression | Operator (@) | Function member | Global function |
@a | + - * & ! ~ ++ -- | A::operator@( ) | operator@(A) |
a@ | ++ -- | A::operator@(int) | operator@(A, int) |
a@b | + - * / % ^ & | < > == != <= >= << >> && || , | A::operator@(B) | operator@(A, B) |
a@b | = += -= *= /= %= ^= &= |= <<= >>= [ ] | A::operator@(B) | - |
a(b, c...) | ( ) | A::operator()(B, C...) | - |
a->b | -> | A::operator->() | - |
8. 关键字this:
关键字this 通常被用在一个class内部,指正在被执行的该class的对象(object)在内存中的地址。它是一个指针,其值永远是自身object的地址。
它可以被用来检查传入一个对象的成员函数的参数是否是该对象本身。
int CDummy::isitme (CDummy& param) { if (&m == this) return 1; else return 0; }
它还经常被用在成员函数operator= 中,用来返回对象的指针(避免使用临时对象)。
CVector& CVector::operator= (const CVector& param) { x=param.x; y=param.y; return *this; }
9. 静态成员:
静态变量:静态数据成员也被称作类变量"class variables",因为它们的内容不依赖于某个对象,对同一个class的所有object具有相同的值。静态成员与全域变量(global variable)具有相同的属性,但它享有类(class)的范围。
静态函数:static函数是全域函数(global functions),但是像一个指定class的对象成员一样被调用。它们只能够引用static 数据,永远不能引用class的非静态(nonstatic)成员。它们也不能够使用关键字this,因为this实际引用了一个对象指针,但这些 static函数却不是任何object的成员,而是class的直接成员。
10. 友元函数(Friend functions):
在类中,用在外部函数申明前加关键字friend,可以允许这个外部访问本类protected 和 private 的成员。
11. 友元类 (Friend classes):
就像我们可以定义一个friend 函数,我们也可以定义一个class是另一个的friend,以便允许第二个class访问第一个class的 protected 和 private 成员。
12. 基类/派生类 父类/子类:
要定义一个类的子类,我们必须在子类的声明中使用冒号(colon)操作符: ,如下所示:
class derived_class_name: public base_class_name;
这里derived_class_name 为子类(derived class)名称,base_class_name 为基类(base class)名称。
当定义一个子类的时候,基类的protected 成员可以被子类的其它成员所使用,然而private 成员就不可以。
下表按照谁能访问总结了不同访问权限类型:
可以访问 | public | protected | private |
本class的成员 | yes | yes | yes |
子类的成员 | yes | yes | no |
非成员 | yes | no | no |
理论上说,子类(drived class)继承了基类(base class)的所有成员,除了:
构造函数Constructor 和析构函数destructor
operator=() 成员
friends
虽然基类的构造函数和析构函数没有被继承,但是当一个子类的object被生成或销毁的时候,其基类的默认构造函数 (即,没有任何参数的构造函数)和析构函数总是被自动调用的。
如果基类没有默认构造函数,或你希望当子类生成新的object时,基类的某个重载的构造函数被调用,你需要在子类的每一个构造函数的定义中指定它。
子类指定了调用父类的构造/析构函数,就按照指定的调用,如果没有指定,就调用父类的默认构造/析构函数。
14. 多重继承:
就是一个子类有多个父类的情况,定义时各父类用逗号’ , ‘分开。
class CRectangle: public CPolygon, public COutput { }
15. 基类的指针:
继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)的指针是type-compatible的。
#include <iostream.h> class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } 执行结果: 20 10
在这个例子中,在主函数 main 中定义了两个指向class CPolygon的对象的指针,即 *ppoly1 和 *ppoly2。 它们被赋值为rect 和 trgl的地址,因为rect 和 trgl是CPolygon 的子类的对象,因此这种赋值是有效的。
使用*ppoly1 和 *ppoly2 取代rect 和trgl 的唯一限制是*ppoly1 和 *ppoly2 是CPolygon* 类型的,因此我们只能够引用CRectangle 和 CTriangle 从基类CPolygon中继承的成员。正是由于这个原因,我们不能够使用*ppoly1 和 *ppoly2 来调用成员函数 area(),而只能使用rect 和 trgl来调用这个函数。
16. 虚拟成员(Virtual members):
如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字virtual ,以便可以使用指针对指向相应的对象进行操作。
#include <iostream.h> class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) { return (0); } }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon poly; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; CPolygon * ppoly3 = &poly; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly3->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; cout << ppoly3->area() << endl; return 0; } 执行结果: 20 10 0
定义了virtual member之后,ppoly1->area()、ppoly2->area()这种形式是允许的。因为area() 被定义为virtual 是因为它后来在子类中被细化了。
17. 抽象基类(Abstract base classes):
在基类中,不对virtual member进行定义,而简单的在函数声明后面写 =0 (等于0),这种函数称为纯虚函数,包含纯虚函数的类称作抽象基类。
class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; /* 纯虚函数 */ };
抽象基类的最大不同是它不能够有实例(对象),但我们可以定义指向它的指针。因此:
CPolygon poly; /* 是不合法的 */
不合法是因为该类包含的纯虚拟函数(pure virtual function) 是没有被实现的,而又不可能生成一个不包含它的所有成员定义的对象。然而,因为这个函数在其子类中被完整的定义了,所以生成一个指向其子类的对象的指针是完全合法的。
抽象类和虚拟成员赋予了C++ 多态(polymorphic)的特征,使得面向对象的编程object-oriented programming成为一个有用的工具。
何为多态,看下例,不必考虑具体是哪个子类,而把area()打印出来:
#include <iostream.h> class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; void printarea (void) { cout << this->area() << endl; } }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); return 0; } 执行结果: 20 10
相关文章推荐
- C++基础知识(二)C++类基础
- C/C++学习笔记:基础知识5
- C++基础知识
- C++基础知识回顾
- C++基础知识(四)—— 操作符/运算符
- C++基础知识
- 学习C及C++中基础知识总结
- C++基础知识复习&总结
- C++基础小知识[2]---堆与栈的区别[转]
- C++ Virtual基础知识讲解
- C++ 基础知识回顾(I/O)
- C++基础知识——变量存储位置
- C++ 基础知识点 一第2章 基本数据类型、运算符与表达式
- c++基础知识
- C++基础知识点
- 『C++』基础知识点
- [C/C++] C语言基础知识:%X是什么意思(未完待续)
- 条形码识别项目中C++以及OpenCV基础知识
- C++基础知识汇总
- 【c++】c++基础知识