孙鑫VC++深入详解第二章学习笔记

第二章 掌握C++

2.1 从结构到类

2.1.1 结构体的定义

  1. C++相比于C的特性:封装性、继承性、多态性;
  2. 对象具有状态和行为,状态保存在成员变量中,行为通过函数实现;
  3. 标准输入输出流对象:cin(>>)默认键盘 和 cout(<<)、cerr(<<)默认显示器;自动根据数据类型调整输入输出格式;
  4. 结构体中的函数称为成员函数。

程序2.1

#include <iostream>
using namespace std;   //error C1083: 无法打开包括文件:“iostream.h”: No such file or directory

struct point
{
	int x, y;           //状态
	void output()       //行为
	{
		cout << "x="<< x << endl << "y=" << y << endl;//endl是换行符
	}
}pt;//注意分号

int main()
{
	cout << "请输入x和y的值:" << endl;
	cin >> pt.x;
	cin >> pt.y;
	pt.output();    //注意括号
	return 0;
}

2.1.2 结构体与类

在C++中,结构体(struct)和类(class)可以通用。
区别在于访问控制的差异:

  1. struct默认访问控制标识符public;public:可以在类的外部进行访问
  2. class默认访问控制标识符private;private:只能在类的内部进行访问
  3. protective。

程序2.2:将程序2.1中的struct point修改为class point

class point   //类:抽象出一些事物共有的属性
{
public:
	int x, y;
	void output()
	{
		cout << "x="<< x << endl << "y=" << y << endl;
	}
}pt;   //实例化了一个对象==类的实例化:具有具体的属性值

2.2 C++的特性

2.2.1 类与对象

类:抽象出一些事物共有的属性;
对象:有具体的属性值。

2.2.2 构造函数

作用:在定义对象的同时,对成员变量进行初始化;
创建对象本身(分配内存)
规定:构造函数的名字和类名相同(唯一性);
没用返回值;
可以有参数。
注意:

  1. 如果一个类没有定义任何构造函数,那么C++会提供一个默认的不带参构造函数。而只要类中定义了一个构造函数,C++便不再提供任何其他构造函数;
  2. 每个类必须有一个构造函数,没有构造函数不能创建任何对象。构造函数如代码2.3所示。

2.2.3 析构函数

~类名(); //对象生命周期结束,释放其占用资源;是一个对象最后调用的成员函数
注意:析构函数不允许有返回值,更不允许有参数,并且一个类中只有一个构造函数。
代码2.3:构造函数和析构函数

class point       //类名:point
{
public:
	int x , y;
    point()      //构造函数  point()
    {
        x = 0;    //在类中定义成员变量时,不能直接给其赋值(如int x=0;),而是在构造函数中赋值(P37提示)
        y = 0;
    }
    ~point()    //析构函数   ~point()
    {
    }
};

2.2.4 函数的重载

条件:函数参数类型、个数不同才能构成重载。
注意:

  1. 只有函数的返回值类型不同,不能重载;
  2. 重载时,注意函数带有默认参数的情况。(P38)
  3. 对比覆盖:重载是发生在同一个类当中;覆盖是发生在父类和子类之间

代码2.4 函数的重载

class point
{
public:
	int x , y;
    point()      //函数1
    {
        x = 0;
        y = 0;
    }
    point(int a, int b)    //重载,函数2
    {
        x = a;
        y = b;
    }    
};

void main()
{
    point pt(5,5);   //C++根据参数类型和个数,执行函数2
}

2.2.5 this指针

  1. this指针是一个隐含的指针,指向对象本身,代表对象的地址。
  2. 用法:当形参的变量名和成员变量的变量名冲突时,可用this->来区别对哪一个变量进行赋值。(P40)

this->x是对成员变量进行赋值;
x是对形参赋值。

2.2.6 类的继承

2.2.6.1 继承

例如:class fish : public animal{};

  1. animal是父类,fish是子类。子类fish以public(公有)的方式继承父类animal。
  2. 子类除了自己的成员变量和成员方法外,还可以继承父类的成员变量和成员方法。
  3. 构造函数和析构函数的调用次序:
    在这里插入图片描述
2.2.6.2 在子类中调用父类带参数的构造函数

例如:父类构造函数: animal(int h , int w){}
则在构造子类时,应该显式地去调用父类的带参数构造函数: fish():animal(400,300){}

2.2.6.3 类的继承及类中成员的访问特性

3种访问权限修饰符:
public:定义的成员可以在任何地方被访问;
protected:定义的成员只能在该类及其子类中访问;
private:定义的成员只能在该类自身中访问。==>不能被子类继承

3种继承方式:
在这里插入图片描述

2.2.6.4 多重继承

定义形式:class B: public C , public D
了解父类表顺序对调用构造函数和析构函数的影响。对于上面的例子,先构造C再构造D,先析构D再析构C。

2.2.7 虚函数与多态性、纯虚函数

2.2.7.1虚函数与多态性

程序 2.5

(P49)...
class animal
{
public:
	...
	void breathe(){cout<<"animal breathe"<<endl;}   /*注意此时不是虚函数*/
};

class fish : public animal
{
public:
	...
    void breathe(){cout<<"fish bubble"<<endl;}
}
void breathetest(animal* pan)
{
	pan->breathe();
}

void main()
{
	animal* pan;  //指向animal类的指针pan
	fish fi;
	pan = &fi;    //将fish类的对象fi的地址直接赋给指向animal类的指针变量pan
	breathetest(pan);
}

/*输出animal breathe*/

1、fish对象也是一个animal类,C++自动进行类型转换;反之,不能把animal对象看成fish对象。

2、当我们将fish转化为animal时,该对象就会被认为是原对象内存模型的上半部分。
在这里插入图片描述

3、将父类函数修改为虚函数,输出结果为fish bubble。

(P49)...
    virtual void breathe()    //虚函数
    ...
/*输出fish bubble*/

虚函数:用virtual关键字申明的函数–>多态性;
迟邦定:编译时并不确定具体调用的函数,而是在运行时依据对象的类型来确认调用的是哪一个函数。

概括(用法):在父类的函数前加上virtual关键字,在子类重写该函数,运行时将会根据对象的实际类型来调用相应的函数。

2.2.7.2 纯虚函数
  1. 纯虚函数不被具体实现,是抽象类,不能实例化对象。
  2. 纯虚函数可以让类先具有一个操作名称,而没有操作内容,在子类继承时再去具体定义。

写法:

virtual void breathe() = 0 ;    //1、等于0;2、无函数体

注意:子类如果有对父类虚函数的覆盖定义,无论该覆盖定义是否有virtual,都是虚函数。

2.2.8函数的覆盖和隐藏

2.2.8.1 覆盖

1、覆盖(P52):发生在父类和子类之间 的 函数完全一样(函数名、参数列表),编译器根据实际类型确定要调用的函数。
2、对比重载:重载是发生在同一个类当中;覆盖是发生在父类和子类之间

程序2.6 函数的覆盖

class animal
{
public:
    virtual void breathe()
    {
        cout<<"animal breathe"<<endl;
    }
};

class fish : public animal
{
public:
	void breathe()     //覆盖
    {
    	cout<<"fish bubble"<<endl;
    }
};

void main()
{
    fish fi;
    fi.breathe();
}
/*输出fish bubble*/
2.2.8.2 隐藏

两种父类函数隐藏的情况:

  1. 子类和父类函数完全相同(函数名、参数列表),但父类函数没有virtual,则父类函数被隐藏。
  2. 子类和父类函数同名,但参数列表不同,则父类函数被隐藏。

区别隐藏与覆盖:覆盖是发生在父类与子类之间,两个函数必须完全相同且都是虚函数。否则就是隐藏。

2.2.9 引用

定义形式:

int a;
int &b = a;  //引用必须在申明时就初始化,与a绑定;后面不会再与其他变量绑定

注意:

  1. 引用只是一个别名,不占用内存空间;此处要和指针区分开来。
  2. 引用多用于函数的形参定义。如程序2.4所示。

程序2.7 引用示例

#include <iostream>
using namespace std;                //换成引用意思表达更清晰:

/*以下为用指针交换数据*/
void change(int* a,int* b);         //void change(int& a,int& b);

int main()
{
	int x = 5, y = 3;
	cout << "x=" << x << endl;
	cout << "y=" << y << endl;
	change(&x,&y);                //是对x和y的地址互换还是对x和y互换?     change(x,y);
	cout << "x=" << x << endl;
	cout << "y=" << y << endl;
	return 0;
}

void change(int* a, int* b)        //void change(int a,int b)
{                                   //{
	int c;                            // int c;
	c = *a;                          //  c = a;
	*a = *b;                          // a = b;
	*b = c;                           // b = c;
}                                   //}

2.2.10 C++类设计习惯和头文件重复包含问题的解决

1、头文件存放类的定义及类成员函数的声明;
源文件存放类中成员函数的实现。

2、注意文件包含;
#include "animal.h" //"",从当前目录开始搜索,然后是系统目录和PATH环境变量所列出的目录
#include <iostream> //<>,从系统目录开始搜索,然后是PATH环境变量所列出的目录。``不搜索当前目录
注意子类的头文件中也要包含父类的头文件,如fish.h中的#include “animal.h”。

3、::是作用域表示符,表明函数或数据成员属于哪个类;
前面如果不跟类名,表示全局函数(即非成员函数)或全局数据。

4、在头文件中声明函数时用了virtual,在源文件中就不用写virtual。

5、要解决类重复定义的问题,要使用条件预处理指令(如课后程序animal.h和fish.h所示)

程序2.8 条件预处理指令解决类重复定义的问题

#ifndef ANIMAL_H_H     //如果没有定义ANIMAL_H_H,就定义ANIMAL_H_H,接着申明animal类
                       //如果定义过了ANIMAL_H_H,直接跳至endif
#define ANIMAL_H_H

class animal
{
};

#endif

2.2.11 VC++程序编译连接的原理与过程

1、编译:头文件不参与编译
源文件单独编译
XXX.obj目标文件
2、链接

编译链接过程如下。
在这里插入图片描述

课后程序

animal.h

/*animal.h*/

#ifndef ANIMAL_H_H     //如果没有定义ANIMAL_H_H,就定义ANIMAL_H_H,接着申明animal类
                       //如果定义过了ANIMAL_H_H,直接跳至endif
#define ANIMAL_H_H

class animal
{
public:
	animal();
	~animal();
	void eat();
	void sleep();
	virtual void breathe();
};

#endif

animal.cpp

/*animal.cpp*/

#include <iostream>
#include "animal.h"

using namespace std;

animal::animal()
{
}

animal::~animal()
{
}

void animal::eat()
{
}

void animal::sleep()
{
}

void animal::breathe()      //在头文件中有virtual,则在源文件中不用加virtual
{
	cout << "animal breathe" << endl;
}


fish.h

/*fish.h*/

#include "animal.h"  //fish类从animal类继承

#ifndef FISH_H_H

#define FISH_H_H
class fish :public animal     //结构体、类名后面没有括号()
{
public:
	void breathe();
};

#endif

fish.cpp

/*fish.cpp*/

#include <iostream>
#include "fish.h"

using namespace std;

void fish::breathe()
{
	cout << "fish bubble" << endl;
}

main.cpp

/*main.cpp*/

#include "animal.h"
#include "fish.h"

void breathetest(animal* pan)
{
	pan->breathe();
}

int main()
{
	animal* pan;
	fish fi;
	animal an;
	pan = &fi;
	breathetest(pan);
	pan = &an;
	breathetest(pan);
	return 0;
}

运行结果
在这里插入图片描述

问题及反思

vs2019编程出现的问题:
1、建立C++空项目后,要在右侧工程的源文件的文件夹下创建XXX.cpp文件;
在这里插入图片描述

否则编译时无法启动程序XXX.exe,系统找不到指定文件。
在这里插入图片描述

2、头文件#include <iostream.h>后添加using namespace std;
否则报错:error C1083: 无法打开包括文件:“iostream.h”: No such file or directory
3、定义结构体或类时,别忘记最后的分号;结构体名或类名后面没有小括号;
4、引用成员函数别忘记括号:pt.output();
5、.sln为项目文件

参考文献

[1]https://blog.csdn.net/weixin_43751983/article/details/91147918this指针
[2]孙鑫.VC++深度详解修订版[M]. 北京:电子工业出版社, 2012. 27-62.

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾宝玉怒撞不周山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值