什么是成员函数
在类中声明的函数就叫做成员函数
考虑用struct实现日期的概念:定义Date的表示方式和操作这种类型的变量的一组函数:
struct Date{
int d, m, y;
};
void init_date(Date &d, int, int, int); //初始化
void add_year(Date &d, int );
void add_month(Date &d, int );
void add_day(Date &d, int );
数据类型,Date和这些函数之间并无显式关联。我们可以通过将函数声明为成员来建立这种关联:
struct Date{
int d, m, y;
//成员函数
void init_date( int, int, int); //初始化
void add_year(int );
void add_month(int );
void add_day( int );
};
由于不同结构可能有同名成员函数,在定义成员函数时必须指定结构名
void Date::init_date( int yy, int mm, int dd){
y = yy;
m = mm;
d = dd;
}
分类
静态/非静态
成员函数是静态或非静态的。
静态成员函数
语法:
说明:
- 静态成员函数不关联到任何对象。调用时,它们无 this 指针。
- 它只能访问类的静态数据成员
- 静态成员函数不能为 virtual、const 或 volatile。
- 静态成员函数的地址可以存储在常规的函数指针中,但不能存储于成员函数指针中。
非静态成员函数
- 非静态成员函数是声明于类的成员说明中,不带 static 或 friend 说明符的函数。
class S {
int mf1(); // 非静态成员函数声明
void mf2() volatile, mf3() &&; // 可为 cv 限定或引用限定
int mf4() const { return data; } // 可内联定义
virtual void mf5() final; // 可为虚函数,可使用 final/override
S() : data(12) {} // 构造函数亦是成员函数
int data;
};
int S::mf1() { return 7; } // 若不内联定义,则必须定义于命名空间
总结:静态成员函数的行为与其他成员函数的行为不同,因为静态成员函数没有隐式this
自变量。 非静态成员函数具有 this
指针。
内联/非内联
内联成员函数
成员函数的定义可以在类中实现,也可以在类外实现(无论是静态的还是非静态的)。
如果在类声明的内部定义一个成员函数,那么该函数会被视为内联函数,并且不需要用其类名来限定函数名称。当然,我们也可以用inline
来显式标记这是一个内联函数
class Account
{
public:
// Deposit是一个内联成员函数
double Deposit( double HowMuch )
{
balance += HowMuch;
return balance;
}
private:
double balance;
};
int main()
{
}
如果成员函数的定义在类声明的外部,则仅当它显式声明为时,才会将其视为内联函数 inline 。 此外,必须通过范围解析运算符 (:😃 用类名称限定定义中的函数名称。
如果下面例子与上面的等效
class Account
{
public:
//声明但是不定义
double Deposit( double HowMuch );
private:
double balance;
};
inline double Account::Deposit( double HowMuch )
{
balance += HowMuch;
return balance;
}
int main()
{
}
包含成员函数的类可具有多个声明,但成员函数本身只能在程序中有一个定义。 多个定义会导致在链接时出现错误消息。 如果类包含内联函数定义,则这些函数定义必须与遵守此“一个定义”规则相同。
非内联成员函数
三个说明符
virtual说明符
virtual
关键字只能用于非静态类成员函数。它表示函数的绑定将推迟到允许时。具体参见虚拟函数
在传统C++中,经常容易发生意外重载虚函数的事情,比如:
struct Base{
virtual void f00();
};
struct SubClass:Base{
void f00();
};
SubClass:foo可能不是程序员尝试重载虚函数,只是恰好加入了一个具有相同名字的函数。另一个可能的情况是,当基类的虚函数被删除后,子类拥有的旧的函数不再重载虚函数而变成了一个普通的类方法,这将造成灾难性后果
override说明符
override
关键字用来指定在基类中重写虚函数的成员函数:当重载虚函数时,引入override关键字将显式的告知编译器进行重载,编译器将检查基函数是否存在这种的虚函数,否则将无法通过编译
struct Base{
virtual void foo(int);
};
struct SubClass : Base{
virtual void foo(int) override; //合法
virtual void foo(float) override; //非法,父类没有此虚函数
};
作用
使用override
有助于防止出现意外的继承行为。下面的示例演示在不使用重写的情况下,可能尚未设计派生类的成员函数行为。编译器不会发出此代码的任何错误:
class BaseClass{
virtual void funcA();
virtual void funcB() const;
virtual void funcC(int = 0);
};
class DerivedClass : public BaseClass{
virtual void funcA(); // ok,按照预期的方式工作
virtual void funcB(); // DerivedClass::funcB()是非 non-const, 所以它没有
// 重写virtual void funcB() const, 它是一个新的成员函数
virtual void func(double = 0.0); // 相比BaseClass::funcC(int), DerivedClass::funcC(double)
//的形参不同,所以它是一个新成员成员
};
使用override
时,编译器会生成错误,而不是以无提示方式创建新的成员
class BaseClass
{
virtual void funcA();
virtual void funcB() const;
virtual void funcC(int = 0);
void funcD();
};
class DerivedClass: public BaseClass
{
virtual void funcA() override; // ok
virtual void funcB() override; // 编译错误:DerivedClass::funcB()不重写BaseClass::funcB() const
virtual void funcC( double = 0.0 ) override; // 编译错误:DerivedClass::funcC(double) 不重写BaseClass::funcC(int)
void funcD() override; // 编译错误: DerivedClass::funcD() 不重写非虚 BaseClass::funcD()
};
若要指定不能重写函数以及无法继承类,请使用final
关键字
final 说明符
finial时为了防止类被继续继承而终止虚函数继续重载引入的。
final
指定不能在派生类中重写的虚函数(override
指定可以重写成员函数的信息)
class BaseClass
{
virtual void func() final;
};
class DerivedClass: public BaseClass
{
virtual void func(); // compiler error: attempting to
// override a final function
};
- 指定无法继承的类。
class BaseClass final
{
};
class DerivedClass: public BaseClass // compiler error: BaseClass is
// marked as non-inheritable
{
};