- C++ 类成员函数
成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。
class Box{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void){
return length * breadth * height;
}
};
double Box::getVolume(void){
return length * breadth * height;
}
-
类访问修饰符:类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的。一个类可以有多个 public、protected 或 private 标记区域,每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。成员和类的默认访问修饰符是 private。
·公有成员:在程序中类的外部是可访问的,可以不使用任何成员函数来设置和获取公有变量的值。
·私有成员:变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
(一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部可以通过调用这些函数来访问私有区域。)
·保护成员:变量或函数与私有成员十分相似,但保护成员在派生类(即子类)中是可访问的。 -
三种继承方式的特点:
·public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
·protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
·**private 继承:**基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private
·有两点不改变:
①private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
②protected 成员可以被派生类访问。 -
类的构造函数的名称与类的名称完全相同,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
·默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。
·程序声明对象时,将自动调用构造函数。
·可以使用初始化列表来初始化字段。
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
·两种使用构造函数来初始化对象的方式:
①显式地调用构造函数。
②隐式地调用构造函数。
Stock food = Stock("World Cabbage",250,1.25);
Stock garment("Furry Mason",50,2.5);//等价于Stock garment=Stock("Furry Mason",50,2.5));
·用构造函数来初始化对象数组的元素时,必须为每个元素调用构造函数。如果类包含多个构造函数,则可以对不同的元素使用不同的构造函数。
const int STKS=4;
Stock stocks[STKS]={
Stock("NanoSmart",12.5,20),
Stock("Boffo objects",200,2.0),
Stock("Monolithic Obelisks",130,3.25),
Stock("Fleep Enterprises",60,6.5)
};
-
对象过期时,程序将自动调用析构函数完成清理工作。如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。析构函数的名称在类名前加上~,不会返回任何值,也不能带有任何参数。
·通常不应在代码中显式地调用析构函数:
①如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用;
②如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块时(该对象是在其中定义的)自动被调用;
③如果对象是通过new创建的,则它将驻留在栈内存或自由存储区中,当使用delete来释放内存时,其析构函数将自动被调用。 -
在类中定义的名称(如类数据成员名和类成员函数名)的作用域都为整个类。作用域为整个类的名称只在该类中是已知的,在类外是不可知的,因此可以在不同类中使用相同的类成员名而不会引起冲突。类作用域意味着不能从外部直接访问类的成员:要调用公有成员函数,必须通过对象;在定义成员函数时,必须使用作用域解析运算符。
·在类中定义常量的方式:
①在类中声明一个枚举。在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。
②使用关键字static。
class Bakery {
private:
enum{Months=12};
double costs[Months];
class Bakery {
private:
static const int Months=12;
double costs[Months];
- 复制构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。复制构造函数通常用于:
·通过使用另一个同类型的对象来初始化新创建的对象。
·复制对象把它作为参数传递给函数。
·复制对象,并从函数返回这个对象。
如果在类中没有定义复制构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个复制构造函数。
classname (const classname &obj) {
// 构造函数的主体
}
- 友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
·友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
·如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend。
class Box {
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};
- this 指针是所有成员函数的隐含参数。在成员函数内部,它可以用来指向调用对象。
友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针。
class Box {
public:
Box(double l=2.0, double b=2.0, double h=2.0){
length = l;
breadth = b;
height = h;
}
double Volume(){
return length * breadth * height;
}
int compare(Box box){
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
-
指向类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,并在使用指针之前对指针进行初始化。
-
可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,意味着无论创建多少个类的对象,静态成员都只有一个副本。
·静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
不能通过对象调用静态成员函数;实际上,静态成员函数甚至不能使用this指针。由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。
int count=String::HowMany();//invoking a static member function
- 继承允许依据另一个类来定义一个类。当创建一个类时不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
·继承代表了 is a 关系,即派生类对象也是一个基类对象。。例如,哺乳动物是动物,狗是哺乳动物,狗是动物等等。
·一个类可以派生自多个类,即可以从多个基类继承数据和函数。定义一个派生类时,使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
class Shape {
public:
void setWidth(int w){
width = w;
}
void setHeight(int h){
height = h;
}
protected:
int width;
int height;
};
class Rectangle: public Shape {
public:
int getArea(){
return (width * height);
}
};
·派生类访问权限:
·派生类继承了所有的基类方法,但下列情况除外:基类的构造函数、析构函数和复制构造函数;基类的重载运算符;基类的友元函数。
·释放对象时,先执行派生类的析构函数,再调用基类的析构函数。
·派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。
·多继承即一个子类可以有多个父类,它继承了多个父类的特性。
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
class PaintCost {
public:
int getCost(int area){
return area * 70;
}
};
class Rectangle: public Shape, public PaintCost {
public:
int getArea(){
return (width * height);
}
};
·基类指针可以在不进行显式类型转换的情况下指向派生类对象;基类引用可以在不进行显式类型转换的情况下引用派生类对象**。但基类指针或引用只能用于调用基类方法。
- 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
·重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。
Box operator+(const Box&);
·重载后的运算符必须至少有一个操作数是用户定义的类型,防止用户为标准类型重载运算符。例如,不能将减法运算符(-)重载为计算两个double值的和,而不是它们的差。
·使用运算符时不能违反运算符原来的句法规则。例如,不能将求模运算符(%)重载成使用一个操作数。
·不能修改运算符的优先级。如果将加号运算符重载成将两个类相加,则新的运算符与原来的加号具有相同的优先级。
·可重载的运算符:
·下面的运算符只能通过成员函数进行重载:=(赋值运算符)、()(函数调用运算符)、[](下标运算符)、->(通过指针访问类成员的运算符)。
·不能重载下面的运算符:
sizeof(sizeof运算符);
.(成员访问运算符);
.*, ->*(成员指针运算符);
::(作用域解析运算符);
?:(条件运算符);
#(预处理符号)
typeid(一个RTTI运算符);
const_cast,dynamic_cast,reinterpret_cast,static_cast(强制类型转换运算符)。
·非成员版本的重载运算符函数所需的形参数目与运算符使用的操作数数目相同;而成员版本所需的参数数目少一个,因为其中的一个操作数是被隐式地传递的调用对象。
Time operator+(const Time&t)const;//member version
Time operator+(const Time&t1,const Time&t2);//nonmember version friend
T1=T2+T3;将转换为下面两个的任何一个:
T1=T2.operator+(T3);//member function
T1=operator+(T2,T3);//nonmember function
·类成员访问运算符( -> )可以被重载,被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。