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限定的方法.
代码例程如下:
-
取消 基本数据类型的cosnt 限定 修改前:
void calculateAngleAndRhoSize(const int map_size_x, const int map_size_y);
修改后:void calculateAngleAndRhoSize(int map_size_x, int map_size_y)
-
指针改引用(引用一定不为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.