设计clas注意事项:头文件、inline、成员初值列、new、Big Three

头文件

#pragma once起到与#include守卫相同的作用,但具有几个优点,包括:减少代码,避免名称冲突,以及提高编译速度.

#pragma和ifndef的区别

#pragma once和#ifdef都是旨在使当前源文件仅在单个编译中包含一次。在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。

首先#pragma once是编译器相关的,有的编译器支持,有的编译器不支持,就是说移植型差。#pragma once则由编译器提供保证:同一个文件不会被编译多次。注意这里所说的“同一个文件”是指物理上的一个文件;
缺点就是,某个头文件有多份拷贝,本方法不能保证他们不被重复包含。

#ifndef方式,这个是C++语言相关,这是C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的。如果写的程序要跨平台,最好使用这种方式。#ifndef由语言支持,移植性好。它依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。另外,为了保证不同头文件中的宏名不冲突,故采取类似于_ABC_H_的取名方式。其中,abc.h为当前头文件名。

#ifndef的方式依赖于宏名字不能冲突,所以这可以保证同一个文件中的内容不会被包含多次,也能保证多份拷贝的文件不会被同时包含;缺点: 就是如果不同内容头文件的宏名不小心“撞车”,编译器则只能包含其中一个,想包含的另一个头文件包不进去。

综上:

#ifndef 保证宏名不重复;
#pragma once保证物理文件不重复;

性能上的区别

   使用 #ifndef 的话,编译器每次看到#include这个文件都需要读入文件,解析代码;
   而使用#pragma once 编译器根本不会重复打开文件, 大大提高了效率。

·编码风格上的区别

   使用#pragma once的代码简洁,重要的是它避免了头文件标号(如_myheader_h_)的 重定义或者 #endif 包含范围错误的情况。

·语意上的区别

   #pragma once是针对文件的,它告诉编译器,本文件只编译一次。
   #ifndef…#define…#endif 只是针对文件中的某一个标号而言的,它能防止三个指令间包含的内容的重复性处理。后者更灵活。

可移植性方面

    #pragma once是微软的开发工具中所使用的,如 msvc 等工具可以完好的支持;
    #ifndef #define #endif是C++标准里面的一部分,对于任何完好支持c/c++的编译器都能使用。显而易见,后者的可移植性更高。

善于使用inline函数

具体参考另一篇文章C++ 中 inline 的用法

使用成员初值列

成员变量初始化有三种方式:

  • 在构造函数体内赋值初始化
  • 在自定义的公有函数体中赋值初始化(一般用于成员变量的初始化)
  • 在构造函数的成员初始化列表初始化

一、构造函数体内初始化

说明:在构造函数体内的初始化方式,本质是是为成员变量赋值,而不是真正意义上的初始化,这点要特别注意!(下面介绍成员初始化列表时会有演示案例对比说明)

class Cperson
{
private:
    int m_age;
    float m_height;
    char* m_name;
public:
    Cperson(int age,float height,const char* name)
    {
        m_age=age;
        m_height=height;
        if(m_name)//先判断当前是否为空
            delete[]  m_name;
        if(name)//如果外部传入的不为空
        {
            int len=strlen(name);
            m_name=new char[len+1];//创建内存
            strcpy(t m_name,name);
        }
        else
             m_name=nullptr;
    }
}

二、自定义的公有函数体中赋值初始化

说明:与构造函数体内初始化方式一样,此种方式本质上也是赋值,而不是初始化

class Cperson
{
private:
    int m_age;
    float m_height;
    char* m_name;
public:
    void setPerson(int age,float height,const char* name)
    {
        m_age=age;
        m_height=height;
        if(m_name)//先判断当前是否为空
            delete[]  m_name;
        if(name)//如果外部传入的不为空
        {
            int len=strlen(name);
            m_name=new char[len+1];//创建内存
            strcpy(t m_name,name);
        }
        else
             m_name=nullptr;
    }
}

三、成员初始化列表初始化

写在构造函数的后面,随着构造函数的执行而执行

初始化顺序:
初始化顺序与书写的在构造函数后的顺序无关,而与成员变量的定义顺序有关(下面有演示案例)初始化列表初始化优先于构造函数内的代码执行顺序

一般只对无动态内存的成员、const成员、引用初始化(const成员、引用成员必须在初始化列表初始化)

成员初始化列表初始化效率更高

有动态内存的成员必须在构造函数内部进行初始化(为什么?因为动态内存不能进行简单的赋值,因此所存在的地址不同,要自己申请动态内存并初始化)。牢记:内部数据内部处理,外部数据外部处理

class Cperson
{
private:
    int m_age;
    float m_height;
    char* m_name;
public:
    Cperson(int age,float height,const char* name);
}
//m_name为指针类型,需要自己申请空间
Cperson::Cperson(int age,float height,const char* name):m_age(age),m_height(height)
{
    if(m_name)//先判断当前是否为空
        delete[]  m_name;
    if(name)//如果外部传入的不为空
    {
        int len=strlen(name);
         m_name=new char[len+1];//创建内存
        strcpy(m_name,name);
    }
    else
         m_name=nullptr;
}
  • 根据上面的三种方式,总结出:成员初始化列表初始化成员才是真正意义上的初始化,其他两种方式都是为赋值
  • 初始化和赋值涉及到底层效率的问题:初始化是直接初始化。而赋值是先初始化一个临时变量,再赋值。前者效率高

参数传递

什么时间传引用、什么时间传值;什么时间返回引用、什么时间返回值;

需要连续赋值??

函数后面加const

当函数内容不发生改变时,一定加上const,因为设计类与使用类的可能不是一个人。当错误使用,编译器会报错,例如下面代码:

#include <iostream>

using namespace std;

class mycomplex {
public:
	mycomplex(double r = 0, double i = 0) :re(r), im(i) {}
	double real() { return re; }
	double imag() { return im; }
private:
	double re, im;
};
int main() {
	const mycomplex c1(2, 1);
	cout << c1.real() << endl;
	cout << c1.imag() << endl;
}

在这里插入图片描述

将ctor放入private中

一种设计模式,单例模式,注意static使用。

调用static函数的方式:

  • 通过对象
  • 通过类名

类的成员函数带有隐藏this指针,但静态成员函数中没有

构造函数、拷贝构造函数、拷贝赋值函数

在这里插入图片描述
为什么必须有copy ctor 和 copy op=?

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
经典写法,但还可以考虑安全性的解法,见《剑指offer》:由于内存不足导致分配失败,可以先进行new新内容,再释放旧内容,分配失败时,实例不会被修改。或者下面的思路:

inline 
String& String::operator=(const String& str){
	if(this!=&str){
		String strTemp(str);
		char* pTemp=strTemp.m_data;
		strTemp.m_data=m_data;
		m_data=pTemp;
	}
	return *this; 
}

将自身实例与临时实例进行交换,由于strTemp是临时对象,运行到if外面会自动析构掉

new和delete过程

C/C++中的new和delete的实现过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值