C++ 类的成员函数

  • 一、普通成员函数
    • 1、普通成员函数的声明
    • 2、普通成员函数的定义方式
    • 3、普通成员函数的调用方式
  • 二、构造函数
    • 1、构造函数的分类
    • 2、构造函数的特点
    • 3、构造函数的作用
    • 4、构造函数何时调用
    • 5、构造函数的调用方式
    • 6、构造函数的定义方式
    • 7、构造函数初始化成员的方式
    • (一)、自定义构造函数
      • 函数原型
    • (二)、默认构造函数
      • 1、函数原型
      • 2、自定义默认构造函数的方式:
      • 3、默认构造函数的注意事项:
      • 4、默认构造函数的作用
    • (三)、拷贝构造函数
      • 1、拷贝构造函数的类型
      • 2、拷贝构造函数的功能
      • 3、拷贝构造函数何时调用
      • 4、默认拷贝构造函数
      • 5、自定义拷贝构造函数
  • 四、析构函数
    • 1、析构函数分类
    • 2、析构函数的特点
    • 3、析构函数的作用
    • 4、析构函数何时调用
    • 5、默认析构函数
    • 6、自定义析构函数
      • 1)、函数原型
      • 2)、何时需要自定义析构函数
  • 五、赋值运算符重载函数
    • 1、赋值运算符的分类
    • 2、赋值运算符的功能
    • 3、何时使用赋值运算符
    • 4、默认的赋值运算符
    • 5、自定义的赋值运算符
  • 六、移动构造函数(C++11)
    • 1、函数原型
    • 2、移动构造函数的原理
    • 3、何时调用移动构造函数
    • 4、隐式声明的移动构造函数
  • 七、移动构赋值运算符函数(C++11)
    • 1、函数原型
    • 2、何时调用移动构赋值运算符函数
    • 3、移动赋值运算符的作用
    • 4、隐式声明的移动赋值运算符
  • 八、静态成员函数(static成员函数)
    • 1、函数原型
    • 2、静态成员函数的特性
  • 九、常成员函数(const成员函数)
    • 1、函数原型
    • 2、常成员函数的特性
    • 3、常成员函数的作用
  • 十、三五法则
    • 1、三法则
    • 2、五法则
    • 3、三五法则

一、普通成员函数

1、普通成员函数的声明

普通成员函数必须在类中声明,声明方式与声明普通函数的方式一样

class People
{public:void getWeight();void getHeight(); //成员函数
};

2、普通成员函数的定义方式

成员函数的定义有两种方式:
1)、在类中定义,此时该函数默认为内联函数;
2)、在类外定义,但要使用作用域限定符(::)

class Rectangle
{private:float height;float width; //数据成员public:float getWidth(){return width; //类中定义,默认为内联函数}float getHeight();
};//类外定义
float Rectangle::getHeight()
{return height;
}

3、普通成员函数的调用方式

1)、通过对象使用成员运算符(.)调用;
2)、通过对象指针使用间接成员运算符(->)调用

二、构造函数

1、构造函数的分类

1)、自定义构造函数(可有参数也可以没有参数);
2)、默认构造函数;
3)、拷贝构造函数

当没有自定义构造函数时,编译器会提供一个默认的构造函数;当自定义构造函数后,编译器不再提供默认构造函数

2、构造函数的特点

1)、构造函数没有返回值和声明类型;
2)、构造函数名与类名相同;
3)、构造函数可以有参数也可以没有参数;
4)、构造函数的函数体可以为空也可以不为空;
5)、构造函数可在类中定义也可在类外定义;
6)、构造函数不能声明为const型的;
7)、构造函数的参数可以有默认值;
8)、类可以有多个构造函数

3、构造函数的作用

用来初始化对象的数据成员

4、构造函数何时调用

构造函数在创建对象时自动调用

5、构造函数的调用方式

1)、显式调用
2)、隐式调用

6、构造函数的定义方式

1)、在类中定义;
2)、可在类外定义,此时需要使用作用域限定符(::)

7、构造函数初始化成员的方式

1)、在函数体内一一赋值初始化;
2)、采用初始化列表初始化

赋值初始化示例:

#include <iostream>
using namespace std;class Student
{private:int m_age;float m_score;public:Student() { }Student(int age, float score){m_age = age;m_score = score;}void show(){cout << "age = " << m_age<< " score = " << m_score<< endl;}
};int main()
{Student stu = Student(16, 90); //显式调用stu.show();Student stu1(18, 96);  //隐式调用stu1.show();return 0;
}

初始化列表示例

#include <iostream>
using namespace std;class Student
{private:int m_age;float m_score;public:Student(int age, float score) : m_age(age), m_score(score){}void show(){cout << "age = " << m_age<< " score = " << m_score<< endl;}
};int main()
{Student stu = Student(16, 90); //显式调用stu.show();Student stu1(18, 96);  //隐式调用stu1.show();return 0;
}

(一)、自定义构造函数

函数原型

className(argument-list)
{... //
}

(二)、默认构造函数

1、函数原型

className()
{}

编译器提供的默认构造函数不接受任何参数,函数体也不执行任何操作;

2、自定义默认构造函数的方式:

1)、给已有的构造函数的所有参数提供默认值;
2)、通过函数重载定义一个没有参数的构造函数

默认构造函数可在类中定义也可在类外定义,在类外定义时需要使用作用域限定符(::)

例如:

class Rectangle
{private:float height;float width; public:Rectangle(float w = 0, float h = 0); //默认构造函数定义方式1Rectangle() //默认构造函数定义方式2{height = 0;width = 0;}
};

3、默认构造函数的注意事项:

1)、如果在类中没有显式定义构造函数,则C++自动提供默认构造函数;
2)、如果显式定义了构造函数,则C++不会再提供默认构造函数,必须为类提供一个默认构造函数;
3)、默认构造函数只能有一个;
4)、设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数;
5)、默认构造函数初始化对象时对象的初始值是未知的

4、默认构造函数的作用

有了默认构造函数(没有参数或所有参数都有默认值),在创建对象的时候就不必初始化对象

#include <iostream>
using namespace std;class Student
{private:int m_age;float m_score;public:Student(){}Student(int age, float score){m_age = age;m_score = score;}void show(){cout << "age = " << m_age<< " score = " << m_score<< endl;}
};int main()
{Student stu; //有了默认构造函数就可以先定义对象再进行初始化,否则就必须在定义的同时进行初始化: Student stu = Student(16, 90);stu = Student(16, 90); stu.show();return 0;
}

(三)、拷贝构造函数

类 T 的拷贝构造函数是非模板构造函数,其首个形参为 T&、const T&、volatile T& 或 const volatile T&,而且要么没有其他形参,要么剩余形参均有默认值
当没有自定义拷贝构造函数时,编译器会提供一个默认的拷贝构造函数;当自定义拷贝构造函数后,编译器不再提供默认拷贝构造函数

1、拷贝构造函数的类型

拷贝构造函数分为两类:
1)、默认拷贝构造函数
2)、自定义拷贝构造函数

2、拷贝构造函数的功能

逐个复制非静态成员的值

3、拷贝构造函数何时调用

拷贝构造函数在以下三种情况下调用:
1)、用类的一个对象初始化另一个对象;
2)、将对象作为实参传递给一个非引用类型的形参(即按值传递);
3)、函数返回对象

4、默认拷贝构造函数

函数原型

className(const className &);

拷贝构造函数说明:
1)、该函数接收一个指向类类型的引用作为参数;
2)、当没有自定义拷贝构造函数时,编译器会提供一个默认的拷贝构造函数作为其类的非 explicit 的 inline public 成员;当自定义拷贝构造函数后,编译器不再提供默认拷贝构造函数;
3)、默认拷贝函数对成员的复制称之为浅拷贝

5、自定义拷贝构造函数

什么情况下需要自定义拷贝构造函数?

当类中有需要用new来分配存储空间的成员时,需要自定义拷贝构造函数,此时的拷贝称之为深拷贝

#include <iostream>
#include <cstring>using namespace std;class Str
{public:char *p;public:Str(char *s){int len = strlen(s);p = new char[len + 1];strcpy(p, s);cout << "调用构造函数" << endl;}Str(const Str &s){int len = strlen(s.p);p = new char[len + 1];strcpy(p, s.p);cout << "调用拷贝构造函数" << endl;}~Str(){if (p != NULL){delete [] p;}cout << "调用析构函数" << endl;}
};int main()
{char s[] = "Hello";Str s1(s);Str s2 = s1;cout << s1.p << endl;cout << s2.p << endl;return 0;
}

四、析构函数

1、析构函数分类

1)、自定义的析构函数;
2)、默认的析构函数
当没有自定义析构函数时,编译器会提供一个默认的析构函数;当自定义析构函数后,编译器不再提供默认拷贝构造函数

2、析构函数的特点

1)、析构函数声明时须在函数名前面加上~;
2)、析构函数没有参数,故不能重载,故类只能有一个析构函数;
3)、析构函数没有返回值和声明类型;
4)、若在类中没有显式定义析构函数,C++会自动提供一个默认的析构函数;若显式定义了析构函数,则不会再提供默认的析构函数;
5)、若某个类作为基类,则一般将析构函数声明为虚析构函数
6)、析构函数可以声明为纯虚,例如对于需要声明为抽象类,但没有其他可声明为纯虚的适合函数的基类;
7)、析构函数可在类中定义也可在类外定义,在类外定义时需要使用作用域限定符(::)

3、析构函数的作用

析构函数用来销毁对象和为对象的数据成员使用new分配的内存空间

4、析构函数何时调用

以下情况下,析构函数是由编译器决定调用
1)、若创建的是静态存储类对象,则析构函数在程序运行结束时被自动调用;
2)、若创建的是自动存储类对象,则析构函数在程序执行完对象所在的代码块时被自动调用;
3)、若对象是通过new创建的,则析构函数在使用delete释放对象占用的内存时被自动调用
4)、若创建的是临时对象,则程序在结束对该对象的使用时调用析构函数

5、默认析构函数

函数原型

~className()
{}

6、自定义析构函数

1)、函数原型

 ~className()
{}virtual ~className() //类作为基类时的通常设为virtual
{}

示例:

#include <iostream>
using namespace std;class Student
{public:Student(){}~Student(){cout << "对象被销毁" << endl;}
};int main()
{{Student stu;Student *sp = new Student;delete sp;}return 0;
}

运行结果:

对象被销毁
对象被销毁

2)、何时需要自定义析构函数

在以下情况下需要自定义的析构函数;
1)、当类中有需要用new来分配存储空间的成员时;
2)、当某个类作为另一个类的基类

五、赋值运算符重载函数

1、赋值运算符的分类

1)、自定义的赋值运算符
2)、编译器提供的默认的赋值运算符

当没有自定义赋值运算符时,编译器会提供一个默认的赋值运算符;当自定义赋值运算符后,编译器不再提供默认赋值运算符

2、赋值运算符的功能

与拷贝构造函数相似,赋值运算符也对非静态成员进行逐个复制,如果成员本身是类对象,则程序将使用为这个类定义的赋值运算符来复制该成员,但静态数据不受影响

3、何时使用赋值运算符

将已有的对象赋给另一个对象时

MyClass foo;
MyClass bar (foo);       // 初始化对象,调用拷贝构造函数
MyClass baz = foo;       // 初始化对象,调用拷贝构造函数foo = bar;               // bar已经被初始化: 调用赋值运算符

4、默认的赋值运算符

函数原型

className & operator=(const className &);

5、自定义的赋值运算符

什么时候需要自定义赋值运算符

1)、用非类类型的值为类类型的对象赋值时(当然,这种情况下我们可以不提供相应的赋值运算符重载函数,而只提供相应的构造函数);
2)、当类中含有指针成员,将同一个类的一个对象赋值给另一个对象时

六、移动构造函数(C++11)

1、函数原型

className (className &&);

类 T 的移动构造函数是非模板构造函数,其首个形参是 T&&、const T&&、volatile T&& 或 const volatile T&&,且无其他形参,或剩余形参均有默认值

2、移动构造函数的原理

拷贝构造函数中,对于指针要采用深拷贝,而移动构造函数中,对于指针采用浅拷贝。类中有指针时浅拷贝之所以危险,是因为两个指针共同指向同一片内存空间,若将第一个指针释放,则另一个指针的指向就合法了。

3、何时调用移动构造函数

同拷贝构造函数

4、隐式声明的移动构造函数

若不对类提供任何用户定义的移动构造函数,且下列各项均为真:
1)、没有用户声明的复制构造函数;
2)、没有用户声明的复制赋值运算符;
3)、没有用户声明的移动赋值运算符;
4)、没有用户声明的析构函数;
则编译器将声明一个移动构造函数,作为其类的非 explicit 的 inline public 成员

#include <iostream>
#include <cstring>using namespace std;class Str
{public:char *p;public:Str(char *s){int len = strlen(s);p = new char[len + 1];strcpy(p, s);cout << "调用构造函数" << endl;}Str(Str &&s) //移动构造函数{p = s.p;s.p = nullptr;cout << "调用移动构造函数" << endl;}~Str(){delete [] p;cout << "调用析构函数" << endl;}
};int main()
{char s[] = "Hello";Str s1(s);Str s2(move(s1));cout << s2.p << endl;return 0;
}

七、移动构赋值运算符函数(C++11)

1、函数原型

className &operator=(className &&);

2、何时调用移动构赋值运算符函数

同赋值运算符

3、移动赋值运算符的作用

执行与移动构造函数相同的工作

4、隐式声明的移动赋值运算符

若不对类类型(struct、class 或 union)提供任何用户定义的移动赋值运算符,且下列各项均为真:
没有用户声明的复制构造函数;
没有用户声明的移动构造函数;
没有用户声明的复制赋值运算符;
没有用户声明的析构函数;
隐式声明的移动赋值运算符不会被定义为弃置,
(C++14 前)
则编译器将声明一个移动赋值运算符,作为其类的 inline public 成员

#include <iostream>
#include <cstring>using namespace std;class Str
{public:char *p;public:Str(){}Str(char *s){int len = strlen(s);p = new char[len + 1];strcpy(p, s);cout << "调用构造函数" << endl;}Str(Str &&s){p = s.p;s.p = nullptr;cout << "调用移动构造函数" << endl;}Str &operator=(Str &&s){p = s.p;s.p = nullptr;cout << "调用移动赋值运算符" << endl;}~Str(){delete [] p;cout << "调用析构函数" << endl;}
};int main()
{char s[] = "Hello";Str s1(s);Str s3;Str s2(move(s1));s3 = move(s2);cout << s3.p << endl;return 0;
}

八、静态成员函数(static成员函数)

1、函数原型

static type functionName(arguments-list)
{... //函数体
}

2、静态成员函数的特性

1)、静态成员遵循类成员访问规则(私有、保护、公开);
2)、静态成员函数不关联到任何对象,即使不定义类的任何对象它们也存在,它们无 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数);
3)、静态成员函数不能为 virtual、const 或 volatile;
4)、静态成员函数的地址可以存储在常规的函数指针中,但不能存储于成员函数指针中;
5)、静态数据成员不能为 mutable;
6)、在命名空间作用域中,若类自身具有外部连接(即不是无名命名空间的成员),则类的静态数据成员也具有外部连接。局部类(定义于函数内部的类)和无名类,包括无名类的成员类,不能拥有静态数据成员;
7)、静态成员函数在类外定义时不需要加关键字static

#include <iostream>
using namespace std;class Student
{public:Student(char *name, int age, float score);void show();public:  //声明静态成员函数static int getTotal();static float getPoints();private:static int m_total;  //总人数static float m_points;  //总成绩private:char *m_name;int m_age;float m_score;
};int Student::m_total = 0;
float Student::m_points = 0.0;Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score)
{m_total++;m_points += score;
}
void Student::show(){cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}//定义静态成员函数
int Student::getTotal(){return m_total;
}float Student::getPoints(){return m_points;
}int main()
{(new Student("小明", 15, 90.6)) -> show();(new Student("李磊", 16, 80.5)) -> show();(new Student("张华", 16, 99.0)) -> show();(new Student("王康", 14, 60.8)) -> show();int total = Student::getTotal();float points = Student::getPoints();cout<< "当前共有" << total<< "名学生,总成绩是" << points<< ",平均分是" << points / total << endl;return 0;
}

运行结果:

小明的年龄是15,成绩是90.6
李磊的年龄是16,成绩是80.5
张华的年龄是16,成绩是99
王康的年龄是14,成绩是60.8
当前共有4名学生,总成绩是330.9,平均分是82.725

九、常成员函数(const成员函数)

1、函数原型

type functionName(arguments-list) const;

2、常成员函数的特性

1)、常成员函数需要在声明和定义的时候在函数头部的结尾加上 const 关键字
2)、常对象只能调用常成员函数,普通对象可以调用所有成员函数;
3)、常成员函数中的const修饰的是该成员函数隐含的this指针,常成员函数中不能改变数据成员的值
4)、const成员函数既可使用const数据,也可使用非const数据,但都不能改变值

#include <iostream>
#include <cstring>using namespace std;class Test
{private:int m_a;int m_b;public:Test(int a, int b) : m_a(a), m_b(b){}int getA() const{return m_a;}int getB() const{return m_b;}
};int main()
{Test test(1, 2);int a = test.getA();int b = test.getB();cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}

3、常成员函数的作用

常成员函数中不得修改类中的任何数据成员的值

例如:

class Complex
{public:Complex();Complex(int real, int image);virtual ~Complex();//编译出错,因为修改了数据成员,去掉const就正确void Print() const{cout << "real = " << ++m_real << " image = " << ++m_image << endl;}//正确void Print() const{cout << "real = " << m_real << " image = " << m_image << endl;}private:int m_real;int m_image;
};

十、三五法则

1、三法则

如果需要析构函数,则一定需要拷贝构造函数和拷贝赋值操作符

2、五法则

在 C++11 标准中,为了支持移动语义,又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”;

3、三五法则

三法则”是针对较旧的 C++89 标准说的,“五法则”是针对较新的 C++11 标准说的;为了统一称呼,后来人们干把它叫做“C++ 三/五法则”

1). 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数;
2). 需要拷贝操作的类也需要赋值操作,反之亦然;
3). 析构函数不能是删除的;
4). 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的;
5). 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作

注:如果在类中没有显式定义以下函数,则编译器自动提供

1)、构造函数
2)、析构函数
3)、拷贝构造函数
4)、赋值运算符
5)、地址运算符

参考:

1、《C++ Primer Plus》
2、《Primer C++》
3、https://zh.cppreference.com
4、http://c.biancheng.net/view/2230.html
5、C++ 三五法则

C++ 类的成员函数相关推荐

  1. 类的成员函数指针和mem_fun适配器的用法

    先来看一个最简单的函数: void foo(int a) {cout << a << endl; } 它的函数指针类型为 void (*)(int); 我们可以这样使用: vo ...

  2. C++对象模型7——类的成员函数、反汇编虚析构函数、RTTI、多态的开销

    一.类成员函数 class test { public:void myfunc(){}virtual void vfunc() {}static void sfunc() {} };void myfu ...

  3. java设计一个顺序表类的成员函数_顺序表代码讲解以及实现

    用C语言编写一个有关顺序表的程序代码 创建一个顺序表,其数据元素类型为整型: 在该顺序表中插入数据(#include #include #define MaxSize 50 typedef char ...

  4. 类中成员函数声明后面的const的含义

    这个const一般是对类中成员函数属性的声明,但这个声明怪怪的,只能放在函数声明的尾部,大概是因为其它地方都已经被占用了.这个声明表示这个函数不会修改类中的任何数据成员.如果在编写const成员函数时 ...

  5. 将类的成员函数作为回调函数(外一篇:友元函数)

    转自:http://blog.csdn.net/xylary/article/details/1548596 将类成员函数用做C回调函数 提出问题:  回调函数是基于C编程的Windows SDK的技 ...

  6. python类的成员函数_Python实现动态添加类的属性或成员函数的解决方法

    某些时候我们需要让类动态的添加属性或方法,比如我们在做插件时就可以采用这种方法.用一个配置文件指定需要加载的模块,可以根据业务扩展任意加入需要的模块. 本文就此简述了Python实现动态添加类的属性或 ...

  7. 第十八章 8string类insert成员函数的使用

    //8 string类insert成员函数的使用 /* #include <iostream> #include <string> using namespace std; i ...

  8. Cpp 对象模型探索 / 类普通成员函数的调用方式

    C++设计时有一个要求,类普通成员函数的调用性能要和全局函数差不多.所以编译器在处理类的普通成员函数的宗旨是将其当作全局函数来处理. 为了达到上述目的,编译器会对类的普通成员函数进行如下操作: 在函数 ...

  9. 浅析C++中的this指针 通过空指针(NULL)可以正确调用一些类的成员函数?

    有下面的一个简单的类: class CNullPointCall { public:     static void Test1();     void Test2();     void Test3 ...

  10. C++学习笔记:类的成员函数的声明与定义

    今天学习一下类的成员函数,首先讲一下常规的类外的函数 写在类的外部的函数叫做全局函数,不属于任何的类. 如果写在类的里面就叫做类的成员函数 这里注意的是,类的成员函数如果加了const,就表明该函数不 ...

最新文章

  1. Containerd 的前世今生和保姆级入门教程
  2. Redis 桌面管理工具 RedisDesktopManager 2020.1 发布
  3. python和php可以一起用吗_Apache同时支持PHP和Python的配置方法
  4. java不用析构函数,堆栈分配的类--C发生不需要的析构函数调用
  5. 【机器视觉学习笔记】OpenCV C++ 调用笔记本摄像头
  6. ECCV2018--点云匹配
  7. excel 中一些单词的意思
  8. LeetCode Letter Combinations of a Phone Number
  9. Windows打印服务器上无法删除打印机
  10. 【1.0】忘记mysql 密码 如何修改之后
  11. 为什么要用 enable_shared_from_this ?
  12. 机器学习笔记(四)BP神经网络模型
  13. 百度开源呼叫中心系统
  14. android x86应用兼容性,x86如何解决Android应用兼容性问题
  15. 数据结构之队列和栈的应用
  16. python买卖股票_python买卖股票的最佳时机(基于贪心_蛮力算法)
  17. string类----猜词游戏
  18. P 哥的桶(线段树+线性基)
  19. 【C++ Primer 学习笔记】: 容器和算法之【泛型算法】
  20. STANet: 基于时空自注意力的遥感图像变化检测模型,提出一个新的大型变化检测数据集LEVIR-CD

热门文章

  1. B站MySQL(尚硅谷)学习笔记
  2. 上海亚商投顾:沪指缩量反弹 一带一路概念股掀涨停潮
  3. Memcache工作原理以及命中率介绍
  4. Java获取当前时间,精确到秒
  5. access自动编号怎么解除_如何在 Access 中重置“自动编号”字段值
  6. 【方向盘】版本历史代码示例之:WebSocket、JSTL
  7. 《快速阅读》读书笔记
  8. 计算机无法转换文件,怎么将heic转换格式 如何打开heic文件
  9. 【微信小程序】蓝牙连接 流程
  10. 三星android 8.0的变化,三星Note8获Android 8.0更新 速度大提升