C++学习-面向对象的高级编程

1、概述

C++学习过程记录。基于侯捷老师的视频。 整体思路:以此为框架,先学习大概内容,如有模糊点,在此框架对应目录出进行补充说明。
学习记录 学习记录 学习记录

2、C++代码的基本内容

2.1 基本组成

一个完整的C++程序一般包括声明部分(.h文件)+程序部分(.cpp文件)以及标准库部分。
在这里插入图片描述
【知识点】

  • 自定义的头文件,使用双引号,见:#include “complex.h”
  • 系统定义的头文件,使用尖括号,见:#include

2.2 防卫式声明

C++中头文件防卫式声明(Header Guard)是一种防止头文件被多次包含的机制,通常用于避免由于头文件的重复包含导致的编译错误。它的实现方式是在头文件的开头和结尾加上一些特定的代码,以确保头文件只被编译一次。
基本格式:
方式1:宏定义

#ifndef _FLLENAME_
#define _FILENAME_
// 头文件内容定义
#endif

方式2:编译器指令

#pragma once

在这两种声明方式中:

  • #ifndef 依赖于宏定义名,当宏已经定义时,#endif之前的代码就会被忽略,但是这里需要注意宏命名重名的问题;
  • #pragma once 只能保证同一个文件不会被编译多次,但是当两个不同的文件内容相同时,仍然会出错。而且这是微软提供的编译器命令,当代码需要跨平台时,需要使用宏定义方式。
    详解:https://www.cnblogs.com/zaiyewujiang/p/17347098.html

2.3 头文件的布局

在这里插入图片描述

2.3.1 前置声明
  • 尽量避免前置声明那些定义在其他项目中的实体.
  • 函数:总是使用 #include.
  • 类模板:优先使用 #include.
2.3.2 class的声明

1)定义声明

/**
*以上部分定义类的头部
**/
class complex
{
    public:
        complex(double x, double y):re(x),im(y){};
        complex& operator += (const complex&);
        double real()const{
            return re;
        };
        double imag() const{
            return im;
        }

    private:
        double re,im;
        friend complex& _doapl(complex*, const complex&);
};

2)类模板的写法

template <typename T>class complex
{
    public:
        complex(T x, T y) : re(x), im(y){};
        complex &operator+=(const complex &);
        T real() const
        {
            return re;
        };
        double imag() const
        {
            return im;
        }

    private:
        T re, im;
        friend complex &_doapl(complex *, const complex &);
};

/****
*使用方式:
complex<double>c1(2.1, 3.4);
complex<int>c2(3,4)
*/

实例:函数模板的调用:

#include <iostream>
#include <stdio.h>
using namespace std;
template <typename T1,typename T2>             //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {
	T2 tmp2 = tmp + tmp1;
	return tmp2;
}
int main(void) {

	cout << "test(5,'A')=" << test<int,char>(5, 'A') << endl;          //<int,char>显式的指明模板的类型
	cout << "test('D',9)=" << test<char,int>('D',9) << endl;           //<int,char>显式的指明模板的类型
	return 0;
}

3、构造函数的定义

以一个函数为例展开:
C中类的构造函数写法有两种,一种是使用初始化列表,一种是在代码块中赋值。
推荐使用初始化列表的方式,列表初始化效率要高于在代码块中直接赋值的方式。
代码1:

class Man{
    public:
  // 默认的构造函数
  Man(){
    cout << "call Man::Man()" << endl;
  }
  // 初始化列表的方式  **关注点**
  Man(string n, int a):name(n),age(a) {
    cout << "call Man::Man(string, int)" << endl;
  }
  // 拷贝构造函数
  Man(const Man& a) {
    cout << "call Man::Man(const Man&)" << endl;
  }
  // 重载了对象赋值操作
  Man& operator=(const Man& a) {
    name = a.name;
    age = a.age;
    cout << "call Man::operator=(const Man& a)" << endl;
    return *this;
  }
private:
  string  name;
  int   age;
};

代码2:

class ChineseMan {
public:
   // 带参数的构造函数,用代码块赋值的方式来初始化   **关注点**
  ChineseMan(Man a, string i) {
    man = a;
    id = i;
  }
private:
  string  id;
  Man   man;
};

代码3:

class ChineseMan {
public:
 // 带参数的构造函数,用初始化列表的方式来初始化   **关注点**
  ChineseMan(Man a, string i):man(a), id(i) {}
private:
  string  id;
  Man   man;
};

main函数测试:

int main()
{
  Man vincent("vincent", 26);
  cout << "-----------" << endl;
  ChineseMan vincent_CN(vincent, "001");
  return 0;
}

以代码2的方式运行:代码块构造

call Man::Man(string, int)
-----------
call Man::Man(const Man&)
call Man::Man()
call Man::operator=(const Man& a)

创建vincent_CN的过程可以分为三步:

  • 首先调用了Man的拷贝构造函数,因为需要把实参vincent拷贝赋值给形参a;
  • 然后又调用了Man的默认构造函数,因为需要创建vincent_CN对象的man成员;
  • 第三步调用赋值运算符函数,把对象a赋值给对象man。至此完成vincent_CN的构造过程;

以代码3的方式运行:初始化列表的方式

  • 第一步调用拷贝构造函数,是因为传递参数需要把实参vincent传递给形参a。
  • 第二步再次调用拷贝构造函数,是用形参a来构造数据成员man。
  • 因此用初始化列表构造对象,调用了两次拷贝构造函数。
    class ChineseMan {
    public:
    ChineseMan(Man a, string i):man(a), id(i) {}
    private:
    string id;
    Man man;
    };

结论: 对比两种方法的执行结果,可以看出来:用初始化列表构造对象,在实参传给形参之后,直接就调用拷贝构造函数用形参来构造数据成员了,不需要经历先构造再赋值的操作。因此效率上确实要比代码块初始化高一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值