- 成员函数(方法)
- 示例1:在结构体中引入成员函数
- 示例2:类
- 示例3:类内定义成员函数
- 示例4:类内声明+类外定义显示内敛的成员函数
- 示例5:类内声明+类外定义成员函数
- 示例6:类与编译期的两遍处理
- 示例7:成员函数与尾随返回类型
- 示例8:使用 this 指针引用当前对象
- 示例9:基于 const 的成员函数
- 补充示例:基于 const 的成员函数重载
- 示例10:成员函数的名称查找与隐藏关系
- 示例11:在静态成员函数中返回静态数据成员
- 示例12:成员函数基于引用限定符的重载
● 可以在结构体中定义函数,作为其成员的一部分:对内操作数据成员,对外提供调用接口
– 在结构体中将数据与相关的成员函数组合在一起将形成类,是 C++ 在 C 基础上引入的概念
– 关键字 class
– 类可视为一种抽象数据类型,通过相应的接口(成员函数)进行交互
– 类本身形成域,称为类域
● 成员函数的声明与定义
了解内联:什么是内联、隐式内联和显式内联
– 类内定义(隐式内联)
– 类内声明 + 类外定义
注意:类内定义的成员函数为内联函数;类内声明 + 类外定义的成员函数默认为费内敛函数,也显示定义为内敛函数。
– 类与编译期的两遍处理
– 成员函数与尾随返回类型( trail returning type )
● 成员函数与 this 指针
– 使用 this 指针引用当前对象
– 基于 const 的成员函数重载
● 成员函数的名称查找与隐藏关系
– 函数内部(包括形参名称)隐藏函数外部
– 类内部名称隐藏类外部
– 使用 this 或域操作符引入依赖型名称查找
● 静态成员函数
– 在静态成员函数中返回静态数据成员
● 成员函数基于引用限定符的重载( C++11 )
示例1:在结构体中引入成员函数#includestruct myStr { int x = 3; void print() { std::cout << x << std::endl; } }; int main() { myStr str; str.print(); }
输出:
3示例2:类
#includeclass myStr { public: int x = 3; void print() { std::cout << x << std::endl; } }; int main() { myStr str; str.print(); }
输出:
3
结构体与类区别:结构体默认public,而class默认private
示例3:类内定义成员函数其中,函数void print()属于类内定义的隐式内联函数。
#includeclass myStr { public: int x = 3; void print() { std::cout << x << std::endl; } }; int main(int argc, char* argv[]) { myStr str; str.print(); }
输出:
3示例4:类内声明+类外定义显示内敛的成员函数
header.h 内类声明
#pragma once #includeclass myStr { public: int x = 3; void print(); }; inline void myStr::print() { std::cout << x << std::endl; }
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str;
str.print();
}
示例5:类内声明+类外定义成员函数
header.h 内类声明
#pragma once #includeclass myStr { public: int x = 3; void print(); };
source.cpp 类外定义
#include "header.h"
void myStr::print()
{
std::cout << x << std::endl;
}
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str;
str.print();
}
示例6:类与编译期的两遍处理
将header.h 头文件改写为
#pragma once #includeclass myStr { public: void print() { std::cout << x << std::endl; } int x = 3; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str;
str.print();
}
当程序执行到header.h 头文件第7行,理论上应该会报错,因为print()中x没有声明。之所以不报错,是由于编译器(编译期的两遍处理)第一遍处理函数和数据,但不处理函数内部的内容,第二次再处理函数内部内容。
示例7:成员函数与尾随返回类型header.h 内类声明
#pragma once #includeclass myStr { public: using myInt = int; myInt x = 3; myInt Add(); };
source.cpp 类外定义
#include "header.h"
// 错误写法:未定义标识符myInt
//myInt myStr::Add()
//{
// return x + 1;
//}
// 正确写法1
//myStr::myInt myStr::Add()
//{
// return x + 1;
//}
// 正确写法2(推荐):尾随返回类型
auto myStr::Add()->myInt
{
return x + 1;
}
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str;
std::cout << str.Add() << std::endl;
}
}
输出:
4示例8:使用 this 指针引用当前对象
将header.h 头文件写为
#pragma once #includeclass myStr { public: void print() // 在成员函数中 隐式的传入了 this { // this指针是存放创建对象的地址; // this类型:this-> myStr *const,这里const表示指针不能被修改,但指针指向的内容可以修改 std::cout << this << std::endl; std::cout << this->x << std::endl; } int x = 0; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;//创建对象1,地址000000DF499FFB44
str1.x = 3;
std::cout << (&str1) << std::endl;
str1.print();
myStr str2;//创建对象2,地址000000DF499FFB64
str2.x = 8;
std::cout << (&str2) << std::endl;
str2.print();
}
输出:
000000DF499FFB44 000000DF499FFB44 3 000000DF499FFB64 000000DF499FFB64 8
补充示例:
将header.h 头文件写为
#pragma once #includeclass myStr { public: void print(int x) { std::cout << x << std::endl;// 函数参数x,主程序输入为8 std::cout << this->x << std::endl;// 对象数据成员x为0 } int x = 0; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
str1.print(8);
}
输出:
8 0示例9:基于 const 的成员函数
将header.h 头文件写为
#pragma once #includeclass myStr { public: // this原本类型:this-> myStr *const void print( ) const // 在成员函数加入这个const,this类型将变为 const myStr *const { // this->x=8; // 打开注释,会报错,提示成员数据无法被修改 std::cout << this->x << std::endl;// 对象数据成员x为0 } int x = 0; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
str1.print();
}
输出:
0补充示例:基于 const 的成员函数重载
将header.h 头文件写为
#pragma once #includeclass myStr { public: void print() //this -> myStr *const { } void print() const //this -> const myStr *const { } int x = 0; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
str1.print();
}
通过这个例子,了解const成员函数重载。
示例10:成员函数的名称查找与隐藏关系成员函数的名称查找优先级:
函数内部(包括形参名称)-> 类内部名称 -> 类外部名称
意思很简单,就是如果成员函数内部存在x,则使用该x;若成员函数内部不存在x,则使用类内部名称;若成员函数内部与类内部名称都不存在x,则使用类外部定义的x。
将header.h 头文件写为
#pragma once #includeint x = 2; class myStr { public: void print() //this -> myStr *const { int x = 0; std::cout << x << std::endl; // 成员函数内部x std::cout << this->x << std::endl;// 类内部名称x std::cout << ::x << std::endl;// 类外部定义的x } int x = 1; };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
str1.print();
}
输出:
0 1 2示例11:在静态成员函数中返回静态数据成员
了解C/C++ 中 static 的用法全局变量与局部变量 | 菜鸟教程 (runoob.com)
将header.h 头文件写为
#pragma once #includeclass myStr { public: myStr() { x++; y += x; } static void print() { std::cout << x << std::endl; } static int gety() { return y; } static int x; static int y; }; // 静态成员数据类外初始化 int myStr::x = 0; int myStr::y = 0;
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
myStr::print();// 统计创建对象的次数 1
myStr str2;
myStr::print();// 统计创建对象的次数 2
// 累加x的值
std::cout << myStr::gety() << std::endl;
}
输出:
1 2 3示例12:成员函数基于引用限定符的重载
将header.h 头文件写为
#pragma once #includeclass myStr { public: void print() & // 左值引用 { } void print() && // 右值引用 { } void print() const & // 常量左值引用 { } void print() const && // 常量右值引用 { } };
main.cpp 调用
#include "header.h"
int main(int argc, char* argv[])
{
myStr str1;
str1.print(); // void myStr::print() &
myStr().print();// void myStr::print() &&
}
参考资料:
《C++ Primer 中文版》(第 5 版)
《C++基础与深度解析》深蓝学院课程
非静态成员函数 - cppreference.com



