类和对象

  • 数据成员的访问
  • 定义成员函数
  • 调用成员函数
  • 私有成员函数
  • 构造函数和析构函数
  • 构造函数成员初始化列表
  • 在构造函数中使用new的注意事项
  • this指针
  • const成员函数
  • 运算符重载
  • 友元函数
  • 拷贝构造函数
    • 隐式转换法
  • 深拷贝与浅拷贝
  • 静态成员变量
  • 静态成员函数

C++面向对象的三大特性:封装、继承和多态。

数据成员的访问

class Point{double x,y;public:int a,b;protected:int c,d;
};

使用class关键字与struct不同,成员在默认情况下是私有的。

  • public:公有,可以由类的用户访问,
  • private:私有,除了对自身的成员函数外都不可见。
  • protected:保护,可以在它的类的子类中被访问。

定义成员函数

如果不想要用户直接访问数据成员,但又可以控制设置它的值,解决方案是使数据成员私有,并且依靠成员函数来提供间接访问。

class Point{public://内联定义函数void set_x(doublex newx){x = newx;}void set_y(double newy){y = newy;}double get_x(){return x;}double get_y(){return y;}
private:double x, y;
};
//在类声明之外定义
void Point::set_x(doublex newx){x = newx;}
void Point::set_y(double newy){y = newy;}
double Point::get_x(){return x;}
double Point::get_y(){return y;}

函数是公开的,可以被用户访问。数据成员是私有的,不能访问。
C++提供两种方式定义成员函数:

  • 内联,一个成员函数可以通过类本身中提供定义,被内联定义。函数原型紧跟要执行的语句的大括号{},与用inline关键字修饰的全局函数一样,内联函数的函数体被直接扩展到调用函数的主体中。(优化运行性能,但是占用更多内存,适用于简短函数)
  • 一个成员函数可以在类声明之外定义。在这种情况下,函数定义要求用作用域符(::)来说明函数的作用域。
    格式:
    返回类型 类名::函数名(参数){ 语句组 }
    在类声明之外定义一个成员函数能使你编写任意长的函数,而无需让类声明本身来负担。

调用成员函数

两种方式:

  • 对象.函数(参数)
  • 通过指向对象的指针:指针->函数(参数)

私有成员函数

私有成员函数和私有数据成员一样,对外隐藏,只能被类中其他函数调用。
函数成员还可以用protected被做成受保护的,此时类的子类可以参考甚至修改(重载)此函数。private和protected的区别只与涉及子类的地方有关

构造函数和析构函数

构造函数是一个初始化函数,当一个对象在内存中被分配后,它会自动被调用。它与类的名称相同,且无返回值。与其他函数一样,可以被内联,也可以在类外定义。
在其他成员函数中可以调用构造函数,但是只是产生一个匿名对象,对本对象并无影响
析构函数是清理函数。作用在对象销毁前系统自动调用,执行清理操作。
都只会被调用一次。

构造函数成员初始化列表

如果Classy是一个类,而mem1、mem2和mem3都是这个类的数据成员,则类构造函数可以使用如下的语法来初始化数据成员:

Classy::Classy(int n, int m) :mem1(n),mem2(0),mem3(n*m+2){...
}

上述代码将mem1初始化为n,mem2初始化为0,将mem3初始化为n*m+2,从概念上说,初始化工作是在对象创建时完成的,此时还未执行括号中的任何代码。注意事项:

  • 这种格式只能用于构造函数
  • 必须用这种格式初始化非静态const数据成员(至少在C++11之前)
  • 必须用这种格式初始化 引用& 数据成员。
  • 数据成员初始化的顺序与它们出现在类中的声明顺序相同,与排列顺序中的顺序无关。

必须用带有初始化列表的构造函数:
1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
2.const成员引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

#include <iostream>
using namespace std;//A 没有默认构造函数(无参构造函数)
class A
{public:A(int k);static int e;
private:const int a=1;const int b;int & c;};
int d = 5;
A::A(int k):b(2),c(d)
{//a = 1;cout << "a = "<<a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;cout << "k = " << k << endl;
}//静态成员变量的初始化:  类型+ 使用类名+作用域限定符
int A::e = 6;class B
{public:B();B(int k);private:A a;int f;
};//因为a无默认构造函数,所以必须在初始化列表中初始化,给a传入参数
B::B(int k):a(k)
{}
B::B() : a(6)
{}int main(void) {A a(7);cout << "A::e = "<<A::e << endl;cout << "a.e = " << a.e << endl;cout << "---------------------" << endl;B b(9);return 0;
}
/**结果:
a = 1
b = 2
c = 5
k = 7
A::e = 6
a.e = 6
---------------------
a = 1
b = 2
c = 5
k = 9*/

在构造函数中使用new的注意事项

  • 如果构造函数中使用的new动态分配内存,则必须提供使用delete的析构函数。
  • new和delet必须相互兼容。new对应delete,new[ ] 对应于delete[]。
  • 如果有多个构造函数,则必须以相同的方式使用new和delete,因为只有一个析构函数,所有构造函数必须跟它兼容。

this指针

this指针指向被调用对象本身。每个非静态成员函数(包括构造函数和析构函数)都有一个this指针。this是调用对象的地址。*this才是被调用对象(解引用)。所以可以使用 this->a,或者(*this).a来调用被调用对象的数据和函数。

const成员函数

保证函数不会修改调用对象。

cosnt Stock land = Stock("Test");
land.show();

对于当前的编译器来说,会对第二行报错,因为show的代码无法保证调用对象不被修改。我们以前通过将函数参数声明为const引用,或者指向const的指针来解决这种问题。但是这里show()方法没有任何参数,它所使用的对象由方法调用隐式地提供。C++的解决方法是将const关键字放在函数括号的后面:

//函数声明
void show() cosnt;
//函数定义
void Stock::show() const

以这种方法声明和定义的函数被称为const成员函数,只要类方法不修改调用对象,就应该将其声明为const。
注意:

  • 常成员函数不能调用其他非常成员函数(静态函数除外)
  • 常成员函数可以修改静态成员变量
  • const对象只能调用常成员函数
  • 两个成员函数的名字和参数表相同,但一个是 const 的,一个不是,则它们算重载
#include <iostream>
#include <vector>
using namespace std;class A
{public:A(int aa = 0):a(aa) {}static int b;void show()const {cout << ++b << endl;setA1();cout << "show const" << endl;//setA();错误}void show() {cout << ++b << endl;setA1();cout << "show" << endl;//setA();错误}void setA() {cout << ++a << endl;}static void setA1() {cout << ++b << endl;}
private:int a;
};int A::b = 1;
int main(void) {A a;const A ca;ca.show();a.show();cout << a.b << endl;cout << ca.b << endl;return 0;
}
/*
2
3
show const
4
5
show
5
5
*/

运算符重载

友元函数

通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限
创建友元函数:
第一步是将其原型放在类声明中,并在类声明前加上关键字 friend:

friend Time test(double n,Time &t);
  • 虽然该函数是在类中声明的,但它不是成员函数,因此不能用成员运算符调用。
  • 虽然友元函数不是成员函数,但是它与成员函数的访问权限相同。
    第二步是在类外编写函数的定义,不需要在定义中使用关键字friend,也不需要使用::限定符。

拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

  1. 用同一类的一个已知的对象去新建并初始化该类的另一个对象
  2. 复制对象把它作为参数传递给函数,值传递—参数为对象。
  3. 复制对象,并从函数返回这个对象—返回值为对象。
  4. 编译器生成临时对象

如果在类中没有定义拷贝构造函数,编译器会自行定义一个(浅拷贝)。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数(深拷贝)。拷贝构造函数的最常见形式如下:

//const关键字是为了防止被拷贝的对象被修改
//使用引用的方式传递参数
classname (const classname &obj) {// 构造函数的主体
}
#include <iostream>using namespace std;class Line
{public:int getLength(void);Line();Line(int len);             // 简单的构造函数Line(const Line &obj);      // 拷贝构造函数~Line();                     // 析构函数private:int *ptr;
};// 成员函数定义,包括构造函数
Line::Line() {cout << "调用无参构造函数" << endl;// 为指针分配内存ptr = new int;*ptr = 0;
}
Line::Line(int len)
{cout << "调用构造函数" << endl;// 为指针分配内存ptr = new int;*ptr = len;
}Line::Line(const Line &obj)
{cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;ptr = new int;*ptr = *obj.ptr; // 拷贝值
}Line::~Line(void)
{cout << "释放内存" << endl;delete ptr;
}
int Line::getLength(void)
{return *ptr;
}void display(Line obj)
{cout << "line 大小 : " << obj.getLength() << endl;
}
Line g() {Line line3(18);return line3;
}
// 程序的主函数
int main()
{Line line1(10);//调用普通构造函数Line line2 = line1;//情况1,同Line line2 = Line(line1);用类的一个已知的对象去初始化该类的另一个对象时,需要调用拷贝构造函数display(line1);//情况2,对象作为参数传递给函数,需要调用拷贝构造函数display(line2);//情况2Line line3 = g();//情况3,从函数返回对象display(line3);//情况2return 0;
}
/*
结果:调用构造函数            //Line line1(10);
调用拷贝构造函数并为指针 ptr 分配内存           //Line line2 = line1;/
调用拷贝构造函数并为指针 ptr 分配内存           //display(line1);
line 大小 : 10                                    //display(line1);
释放内存                                        //display(line1);
调用拷贝构造函数并为指针 ptr 分配内存           //display(line2);
line 大小 : 10                                    //display(line2);
释放内存                                        //display(line2);
调用构造函数                                  //Line line3 = g();
调用拷贝构造函数并为指针 ptr 分配内存           //Line line3 = g();
释放内存                                        //Line line3 = g();
调用拷贝构造函数并为指针 ptr 分配内存           //display(line3);
line 大小 : 18                                    display(line3);
释放内存                                        display(line3);
释放内存            //line3
释放内存            //line2
释放内存            //line1
*/


不要用拷贝构造函数初始化匿名对象

 Line line1(10);//错误,“Line line1”: 重定义//编译器会认为 Line (line1) == Line line1Line(line1);

隐式转换法

 Line line1 = 10;//等于Line line1 = Line(10);

默认情况下,C++编译器会给一个类添加三个函数:

  • 默认构造
  • 默认拷贝构造
  • 默认析构
    如果我们写了有参构造函数,编译器就不再提供默认构造;但会提供默认拷贝函数。
    如果我们写了拷贝构造函数,编译器就不再提供其他构造函数;

    拷贝构造 > 有参构造 > 默认构造

深拷贝与浅拷贝

默认的拷贝构造函数—浅拷贝。复制的是成员的值。按字节拷贝,当对象的数据资源是由指针指向的堆时,默认的拷贝构造函数只是将指针复制。 ,多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏
深拷贝:深拷贝会重新开辟内存空间。,每个对象拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。

静态成员变量

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
class Point
{public:static int npoints;//特殊情况,用const和static一起修饰,只可以在类中声明一次const static int a = 0;
};
//static变量必须在类外定义一次,并只定义一次
int Point::npoints = 0;
int main(void) {Point p1, p2, p3;Point::npoints++;p1.npoints++;p2.npoints += 5;cout << p3.npoints << endl;//p3.npoints == 7return 0;
}

注意:不能在类声明中初始化静态成员变量,这是因为声明只描述了如何分配内存,但并不分配内存。例外情况:静态数据成员为const整数类型或枚举型。
数据通常是由单个对象所拥有的,而不是类拥有。然而它可以声明由同一个类的所有对象共享的一个或多个数据成员,无论创建了多少对象,程序都只创建一个静态类变量副本,这样的数据成员称为静态的。
成员函数也可以是静态的,静态成员函数的定义和声明和其他函数一样,但是用途受限,不能引用this指针,不能引用非静态成员函数。
静态成员可以是公有私有和受保护的。

class Student{public:Student(char *name, int age, float score);void show();
public:static int m_total;  //静态成员变量
private:char *m_name;int m_age;float m_score;
};

static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,也只为 m_total 分配一份内存,所有对象使用的都是这份内存中的数据。当某个对象修改了 m_total,也会影响到其他对象。

static 成员变量必须在类声明的外部初始化,具体形式为:

type class::name = value;

type 是变量的类型,class 是类名,name 是变量名,value 是初始值。将上面的 m_total 初始化:

int Student::m_total = 0;

静态成员变量在初始化时不能再加 static,但必须要有数据类型。被 private、protected、public 修饰的静态成员变量都可以用这种方式初始化。
注意:static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配。反过来说,没有在类外初始化的 static 成员变量不能使用。
static 成员变量既可以通过对象来访问,也可以通过类来访问。
static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问

总结:

  1. 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。

  2. static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。

  3. 静态成员变量必须初始化,而且只能在类体外进行。例如:
    int Student::m_total = 10;
    初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。

  4. 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。

静态成员函数

静态成员函数(static)不能通过对象来调用,也不能使用this指针。函数声明必须包含关键字static,但如果函数定义是独立的,则其中不能包含关键字static。如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符::来调用它。

//在Test类中声明和定义
static int howmany(){ return num_string;}
//调用:
int count = Test::howmany;

由于静态成员函数不与特定的对象相关联。因此只能使用静态数据成员

C++类和对象成员函数,静态成员,构造函数和析构函数、初始化列表相关推荐

  1. (C++题目)定义一个描述学生基本情况的类Student,数据成员包括姓名、学号、英语成绩和高数成绩;成员函数包括构造函数、析构函数、获取姓名、获取学号、求出平均成绩,以及显示各

    定义一个描述学生基本情况的类Student,数据成员包括姓名.学号.英语成绩和高数成绩:成员函数包括构造函数.析构函数.获取姓名.获取学号.求出平均成绩,以及显示各科成绩和平均成绩的显示函数.编写ma ...

  2. 类与对象:类的6个默认成员函数: 构造函数、析构函数、拷贝构造函数、赋值操作符重载、默认拷贝构造与赋值运算符重载的问题、const成员函数、 取地址及const取地址操作符重载

    1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类.任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数. 构造函数 析构函数 拷贝构造函数 赋值操作符重载 const成员函数 ...

  3. 类的6个默认成员函数:构造函数、析构函数、拷贝构造函数、重载运算符、三/五法则

    文章目录 6个默认成员函数 构造函数 概念 默认构造函数的类型 默认实参 概念 默认实参的使用 默认实参声明 全局变量作为默认实参 某些类不能依赖于编译器合成的默认构造函数 第一个原因 第二个原因 第 ...

  4. 3-3:类与对象中篇——默认成员函数之构造函数和析构函数

    文章目录 一:类的默认6个成员函数 二:构造函数 (1)构造函数的概念 (2)构造函数注意事项 三:析构函数 (1)析构函数的概念 (2)析构函数注意事项 一:类的默认6个成员函数 如果一个类里面什么 ...

  5. 【C++从入门到踹门】第三篇:类和对象(中)类的默认成员函数

    目录 1.类的默认成员函数 2.构造函数 2.1 构造函数引入 2.2 构造函数概念及特点 3. 析构函数 3.1 析构函数引入 3.2 析构函数的概念 3.3 在哪些情况下会程序会执行析构函数? 3 ...

  6. C++ 学习 ::【基础篇:13】:C++ 类的基本成员函数:类类型成员的初始化与构造函数问题

    本系列 C++ 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C++ 学习系列将分为三个阶段:基础篇.STL 篇.高阶数据结构与算法篇,相关重点内容如下: 基础篇:类与对象(涉及C++的三大特 ...

  7. C++>继承,继承方式及其比较,子类和父类对象指针,派生类的默认成员函数,虚继承,继承与友元,继承与静态成员

    继承 继承的概念与定义 概念: 继承是指一个子类(或称为派生类)继承父类(或称为基类)的特征(属性和操作).继承是面向对象程序设计时实现代码复用的重要手段,它允许在原有的类的基础上进行扩展,增加功能, ...

  8. C++模板学习02(类模板)(类模板语法、类模板与函数模板的区别、类模板中的成员函数创建时机、类模板对象做函数参数、类模板与继承、类模板成员函数类外实现、类模板分文件编写、类模板与友元)

    C++引用详情(引用的基本语法,注意事项,做函数的参数以及引用的本质,常量引用) 函数高级C++(函数的默认参数,函数的占位参数,函数重载的基本语法以及注意事项) C++类和对象-封装(属性和行为作为 ...

  9. c++中delete对象后 调用成员函数_C++类的特殊成员函数及default/delete特性

    本文包含以下内容 1. C++的四类特殊成员函数介绍,重点介绍拷贝构造函数和拷贝复制运算符 2. C++11中的default/delete特性 本文内容侧重个人理解,深入理解其原理推荐https:/ ...

最新文章

  1. JVM1.6 GC详解
  2. Disruptor并发框架-2
  3. leetcode 81 Search in Rotated Sorted Array II ----- java
  4. LeetCode 2131. 连接两字母单词得到的最长回文串
  5. 前端常用60余种工具方法(上)
  6. linux实现内存共享,Linux共享内存实现
  7. Java多线程详解(如何创建线程)
  8. 服务器连接池怎么配置文件,服务器连接池怎么配置
  9. 用条件变量实现事件等待器的正确与错误做法
  10. 动态规划经典例题汇总 (附最全题目链接)
  11. python 归纳 (十八)_队列Queue在多线程中使用(二)
  12. 基于留一法的快速KNN代码
  13. Java中的抽象类与abstract关键字
  14. element拼音模糊搜索
  15. python好用的内置库_python内置的高效好用各种库
  16. Windows 10系统中修改用户名的方法
  17. 微信消息模板换行符转义问题处理
  18. 毕业生的找工作的时候住宿问题是如何解决的
  19. initramfs概述
  20. figma:使用mac上的字体 | 转换ttc字体文件

热门文章

  1. 乔布斯的13句经典妙语
  2. oracle中overwrite写法,Oracle11g新增事件CLIENTID_OVERWRITE
  3. MJPEG协议入门介绍
  4. c程序设计语言实现itoa,C语言中实现itoa函数的实例
  5. Python基础学习:stat模块
  6. 将https替换成http
  7. 海悦生活冲刺上市:外拓压力大,表现不及建发物业,还有项目亏损
  8. 管理系统开发一: winform连接sql数据库
  9. xgplayer自定义播放器样式
  10. 我的10年软件情缘--2001到2011