C++函数的参数与返回

C++函数的参数与返回

目录

函数的参数与返回

一、函数的参数

1.3种类型的c++函数参数

基本数据类型(内建类型)、指针或引用、类(结构体).  具体说明如下表:

类型 大小 例子
基本数据类型 固定长度.不超过8字节(少用的long double 可能除外) char、 int、 unsigned int、long、float、double, enum 等
指针或引用 固定长度.一般为cpu的字长(32位机4字节 64位机8字节)特殊:有的单片机24位寻址(基本少遇到) char *,int * , char &, int &
类(结构体) 由类成员决定,不同的类不一样.最小一般1字节 (空结构体大多数编译器,强制1字节) class A; struct B;
类型 当函数传入参数时
基本数据类型 直接用本类型,不要用const修饰 建议不要修饰,函数内部修改不会传出)建议: void func(int a)不建议: void func(const int a)
指针或引用 能用引用传入就不要用指针.能用const 限定一定要添加.建议 : void func(const int &a) 或 void func(int &a)万不得已 : void func(const int *pa) 或 void func(int *pa) (最差!!)  注意:非本类指针输入需要检查是否为NULL
类(结构体) 用指针或者引用作为参数时,不建议直接用类作为参数(特例:类成员个数少)class A{int m, int d;}建议: void func(const A &a) 或 void func(A &a)不建议:void func(A a)  万不得已 : void func(const A *pa) 或 void func(A *pa) (最差!!)  注意:非本类指针参数需要检查是否为NULL

2.const 限定补充:

1.类(结构体)类型参数时表示函数内部不修改该变量的数据. 能达到保护参数数据不被修改的作用,从而减少的类或者数据的复制,以达到降低cpu与内存的效果. 示例如下:

  • 有效的const 限定: const A &a, const A *pa, const A *const pa (第一种最推荐!)
  • 也可以但不建议: A const &a, A const *pa

2.类的函数为const时该函数称为const函数.表示该函数不修改类的成员变量(可用读),有以下优点:

  • 如果参数类型为const &或者 const *类型,只能调用const 的成员函数.(因此能被const限定方式调用)
  • 可以和非const 函数重载,达到既能调用非const 限定的任何方法,也能调用const限定的方法.

代码例程如下:

  1. 取消 基本数据类型的cosnt 限定 修改前: void calculateAngleAndRhoSize(const int map_size_x, const int map_size_y); 修改后: void calculateAngleAndRhoSize(int map_size_x, int map_size_y)

  2. 指针改引用(引用一定不为NULL,指针需要检查,否则可能引发段错误) 修改前: virtual void createObstacle( CDilemmaData* data); 修改后: virtual void createObstacle( CDilemmaData &data);

3.加引用和加非const函数(大量优化速度!!)

修改前如下:

void CCoveragePointGeneration::getCoveragePoint(
            algo::CLocalSection& local_section, std::deque<algo::CChainCell>& up_deque_indent,
            std::deque<algo::CChainCell>& down_deque_indent, 
            std::deque<std::deque<algo::CChainCell>>& coverage_deque_up_split,
            std::deque<std::deque<algo::CChainCell>>& coverage_deque_down_split)
{
    .... //存在多次对大数据进行复制

    CCoverageData cover_data = local_section.getCoverageData();  //复制第一次
    algo::CChainMap chain_map = cover_data.getMap(); //复制第二次

    sparseCoveragePoint(cover_data, chain_map, coverage_deque_up_split, coverage_deque_down_split);

    cover_data.setMap(chain_map); //复制第三次
    local_section.setCoverageData(cover_data); //复制第四次
}

void CCoveragePointGeneration::sparseCoveragePoint(
        const CCoverageData& cover_data, algo::CChainMap& map,
        std::deque<std::deque<algo::CChainCell>>& coverage_up_deque,
        std::deque<std::deque<algo::CChainCell>>& coverage_down_deque) const //const 非常棒的限定,外部可以安心调用
{
  ....
}

修改后如下:

class CCoverageData //本类所有成员超过480K大小(复制成本高昂)
{
    ....
    const algo::CChainMap& getMap() const //如果为const 方法一定要加该限定
    {
        return m_map;
    }

    algo::CChainMap& getMap() //添加非const方法和const方法重载
    {
        return m_map;
    }
};

class CLocalSection
{
    ....
    const CCoverageData& getCoverageData() const
    {
        return m_coverage_data;
    }

    CCoverageData& getCoverageData() //添加非const方法和const方法重载
    {
        return m_coverage_data;
    }
    //建议getCoverageData()  改名为 coverageData()
    //习惯使用: 获取参数直接名字(不用get修饰),设置时使用 set修饰

    /*  效率低下,且功能单一通过 普通getCoverageData()和 const方法配合能灵活且高效!
    void setMap( const algo::CChainMap& map)
    {
        m_map = map;
    }
    */
};


void CCoveragePointGeneration::getMoppingCoveragePoint(
    algo::CLocalSection& local_section, std::deque<algo::CChainCell>& up_deque_indent,
    std::deque<algo::CChainCell>& down_deque_indent, std::deque<std::deque<algo::CChainCell>>& coverage_deque_up_split,
    std::deque<std::deque<algo::CChainCell>>& coverage_deque_down_split) const 
{
    reverseCoverageDirection(local_section, up_deque_indent, down_deque_indent, coverage_deque_up_split,
                             coverage_deque_down_split);

    //如果不想函数修改成员也可以 auto const &cover_data = local_section.getCoverageData(); 
    //atuo 自动推断. 简洁 等同于 CCoverageData &cover_data = local_section.getCoverageData()
    auto &cover_data = local_section.getCoverageData(); //没有复制任何数据. 调用 getCoverageData() 非const 方法
        

    sparseMoppingCoveragePoint(cover_data, cover_data.getMap(), coverage_deque_up_split, coverage_deque_down_split);
    //直接修改(效率大幅度提高) 
}

4. 函数参数的输入输出顺序

在代码圈中留下2中习惯, 先全部为输出,之后全部为输入. 或者先全部为输入,之后全部为输出.很少混合出现!!! 我个人喜欢第二种, 将输出放前,它具有以下优点:

  • 优点一: 让改变的值更醒目
  • 优点二: 输出值一般少用输入值, 更容易看到分界线.

如果参数没加const 限定,默认都是要被修改的.如果全部类没写限定,会呈现难以确定那个参数是输入值那个参数是输出值的现象.因此只能通过复制类的方法输入.最终引起大量消耗空间和CPU,并造成代码难以维护且低效的恶果.

二、返回值

对返回值可以不限定类型,如果返回为类,编译器自动会优化,达到类似输入参数的目的,c++11还可以自动调用 右值复制.(可以搜索 返回值优化 RVO) 如下:

 std::deque< CLineInfor > CMeasurePolygon::getPolygonLine(const std::deque< CLineInfor >& LineListInfor,
const CLineSegment& currentLine)
{
      std::deque< CLineSegment > otherLineList;

     ...
    return otherLineList; //非常高效,放心使用  
}

如果对于类的成员,外部需要获取建议用 const 函数限定,还可以添加非const函数. 大多数情况下,外部只需要读数据进行读取,如果用直接返回类就过于低效.

修改前如下:

class CChainExtraction
{
     ...
     CChain getLiveChain() //低效(外部可能只是读取)
    {
        return m_current_chain;
    }

    void clearLiveChain() //功能太单一,如果还有其它功能还得继续写函数
    {
        m_current_chain.clearList();
    }

   void setLiveChain(const CChain &val)  //不高效的做法
    {
        m_current_chain = val;  //一定调用复制构造函数
    }

   CChain  m_current_chain;
};

修改后如下:

class CChainExtraction
{
    const CChain &getLiveChain() const
    {
        return m_current_chain;
    }

    CChain &getLiveChain()
    {
        return m_current_chain;
    }

/*
    clearLiveChain()
    可以用 getLiveChain().clear(); //替换
*/

/*
    void setLiveChain(const CChain &val)
    可以 getLiveChain() = val; //替换 某些清空下还可以不调用复制函数
    //如果取消get开始更完美

*/

};

三、const 滥用

const 用于函数的输入参数时,表示本函数不对输入的参数进行修改.但对于基本数据类型类型时,函数内部修改与否都不对外部造成影响.(对函数内存处理限定有作用,一般不用限定) 基础类型本身为cpu字长,不需要引用进行寻址(32位机某些类型除外),因此用于被滥用. 下例:

//修改前
class TA
{
    void setEn(const enum Ten &a)
    { 
        m_en = a;
    }
     
    const Ten &getEn()
    {
        return m_en;
    }
     
    Ten m_en; 
};

//修改后
class TA
{ //注意:其它基本类型也相似
    void setEn(Ten a) //enum 可以省略.同样如果struct修饰也可以省略
    { 
        m_en = a;
    }
     
    Ten getEn() const //const限定方法, 本身字长不需要用都返回限定
    {
        return m_en;
    }
     
    Ten m_en; 
};

四、总结

  • 如果参数能用引用绝不用指针!!
  • 如果参数能用 const 限定就必须加const 限定!!
  • 如果不得不用指针参数,请检查指针是否为空!
  • 如果能用const限定类方法,一定要采用const 限定!
  • 如果能用static限定类方法,一定要采样static 限定!(后问会说)
  • 如果能返回用引用获取值(类的成员)绝不用复制获取!
  • 如果返回为局部类,请大胆直接返回(不能返回引用)!!
  • 如果能将函数或成员用private或者protected修饰,一定不要用public.