C++中,函数声明时指针、引用相关的语义,定义方法原型时参考。
2008-09-16 20:57
661 查看
一门语言,包括语法、语义。通常计算机书籍上只会讲语法规则。这几年下来,发现讲语义的书籍太少太少(也许是我很久不看书了^_^)。本文抛砖引玉,罗列一些与函数声明相关的常见语义。
函数在调用时,将参数压栈,返回值则拷贝给调用者。如果是简单类型,事情很简单,但是牵扯到指针或者引用,事情就会复杂一些,因为在C/C++中,由程序员负责管理内存和资源,一个优秀的程序员必须清楚的了解自己代码内,每一次内存申请及对应的释放在什么时候发生。
比如下面这个声明:
struct MyDate
{
int year;
int month;
int day;
};
void GetBirthday(MyDate * pBirthday);
请问pBirthday这个指针,谁负责申请空间,谁负责释放空间?
这个问题其实比较简单,因为参数压栈时将指针本身压栈,指针所指地址并不在栈中,显然应该由调用者负责申请内存,对应的,应该由调用者释放内存。
如果我们把函数声明改一下,如果返回一个指针怎么理解?
MyDate * GetBirthday(void);
常规理解,函数负责申请内存,调用者负责释放。
但是。。。但是。。。如果GetBirthday()的实现不常规呢?
static MyDate myBirthday;
...
...
MyDate * GetBirthday(void)
{
return &myBirthday;
}
如果调用者delete 返回的指针,程序行为不确定。嘿嘿,所谓行为不确定,就是在你这里程序运行好好的,在客户那里一定会崩溃的行为。
可能有看官要问,为啥返回一个指针呢?为啥不直接返回MyDate对象呢?
在返回对象时,编译器会为我们生成一个不可见的临时对象,供调用者使用,所以如果声明成
MyDate GetBirthday(void);
与返回指针的声明相比,在C代码中,会多一次结构拷贝,在C++代码里,会多一次拷贝构造,多一次析构。在某些追求效率的场合,这些多余的操作很难容忍。此外,如果你的对象里有资源、内存管理,在析构时很有可能将资源、内存意外释放掉。
有同学说,反正是公司内部的代码,调用者看一下实现不就结了吗?
问题在于:
1、你要求别人看你的实现,增加别人的负担,你是在给别人挖坑;
2、如果你的代码是编译成库,供别人使用怎么办?你的公司不愿意泄露源代码怎么办?
作为折衷,可以把函数声明成
const MyDate * GetBirthday(void);
const的语义,就是告诉调用者,这个指针归我管,你不要乱动。
由于对象传递时编译器会自动生成拷贝语句,所以往往我们都是传递对象指针或者对象引用,下面罗列一些常见的相关语义,供大家参考:
一个方法接受普通指针,表明这个方法可能会修改这个指针指向的数据;
一个方法接受常量指针,表明这个方法承诺不会修改这个指针所指向的数据;
一个方法返回普通指针,表明这个方法要求调用者负责释放这个指针;
一个方法返回常量指针,表明这个方法会自己处理这个指针所指空间,调用者不需要费心;
一个方法接受普通引用,表明这个方法可能会修改这个引用的数据;
一个方法接受常量引用,表明这个方法承诺不会修改这个引用的数据;
一个方法返回普通引用,表明这个方法期望或者允许调用者修改返回的对象;
一个方法返回常量引用,表明这个方法不允许调用者修改返回的对象;
这些东西看着很琐碎,有同学可能会说:这么多条条框框,编码彻底成了体力活儿。
但是没办法,我们要注意,代码是写给人看的,不是写给编译器看的,切记切记。
函数在调用时,将参数压栈,返回值则拷贝给调用者。如果是简单类型,事情很简单,但是牵扯到指针或者引用,事情就会复杂一些,因为在C/C++中,由程序员负责管理内存和资源,一个优秀的程序员必须清楚的了解自己代码内,每一次内存申请及对应的释放在什么时候发生。
比如下面这个声明:
struct MyDate
{
int year;
int month;
int day;
};
void GetBirthday(MyDate * pBirthday);
请问pBirthday这个指针,谁负责申请空间,谁负责释放空间?
这个问题其实比较简单,因为参数压栈时将指针本身压栈,指针所指地址并不在栈中,显然应该由调用者负责申请内存,对应的,应该由调用者释放内存。
如果我们把函数声明改一下,如果返回一个指针怎么理解?
MyDate * GetBirthday(void);
常规理解,函数负责申请内存,调用者负责释放。
但是。。。但是。。。如果GetBirthday()的实现不常规呢?
static MyDate myBirthday;
...
...
MyDate * GetBirthday(void)
{
return &myBirthday;
}
如果调用者delete 返回的指针,程序行为不确定。嘿嘿,所谓行为不确定,就是在你这里程序运行好好的,在客户那里一定会崩溃的行为。
可能有看官要问,为啥返回一个指针呢?为啥不直接返回MyDate对象呢?
在返回对象时,编译器会为我们生成一个不可见的临时对象,供调用者使用,所以如果声明成
MyDate GetBirthday(void);
与返回指针的声明相比,在C代码中,会多一次结构拷贝,在C++代码里,会多一次拷贝构造,多一次析构。在某些追求效率的场合,这些多余的操作很难容忍。此外,如果你的对象里有资源、内存管理,在析构时很有可能将资源、内存意外释放掉。
有同学说,反正是公司内部的代码,调用者看一下实现不就结了吗?
问题在于:
1、你要求别人看你的实现,增加别人的负担,你是在给别人挖坑;
2、如果你的代码是编译成库,供别人使用怎么办?你的公司不愿意泄露源代码怎么办?
作为折衷,可以把函数声明成
const MyDate * GetBirthday(void);
const的语义,就是告诉调用者,这个指针归我管,你不要乱动。
由于对象传递时编译器会自动生成拷贝语句,所以往往我们都是传递对象指针或者对象引用,下面罗列一些常见的相关语义,供大家参考:
一个方法接受普通指针,表明这个方法可能会修改这个指针指向的数据;
一个方法接受常量指针,表明这个方法承诺不会修改这个指针所指向的数据;
一个方法返回普通指针,表明这个方法要求调用者负责释放这个指针;
一个方法返回常量指针,表明这个方法会自己处理这个指针所指空间,调用者不需要费心;
一个方法接受普通引用,表明这个方法可能会修改这个引用的数据;
一个方法接受常量引用,表明这个方法承诺不会修改这个引用的数据;
一个方法返回普通引用,表明这个方法期望或者允许调用者修改返回的对象;
一个方法返回常量引用,表明这个方法不允许调用者修改返回的对象;
这些东西看着很琐碎,有同学可能会说:这么多条条框框,编码彻底成了体力活儿。
但是没办法,我们要注意,代码是写给人看的,不是写给编译器看的,切记切记。
相关文章推荐
- c++学习笔记之基础---类内声明函数后在类外定义的一种方法
- C++的三种传递参数到函数的方法:按值传递,用引用参数按引用传递,用指针参数按引用传递
- c++中类定义中,对象,引用,指针等针对函数的选择顺序
- C/C++_log2000_函数声明与指针定义等零散知识点随手笔记
- C++中,类和函数分开定义声明和实现的方法
- 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针
- C/C++语言参数传递----函数/方法 参数的指针引用传递
- C++ 函数指针的定义方法及使用
- C++中重构函数声明定义与static变量使用方法
- C++简单学习、字符(串)字面值、声明和定义、引用和指针
- C++基础2 引用 函数扩展: 默认值 占位符 指针 重载 类:引用类指针 声明实现分开写
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- C++文本查询程序 定义类管理数据 用引用共享数据 不用智能指针 C++Primer练习12.27
- c++ 指针、引用、内存管理、函数(引用、重载、内敛)——c++复习(一)
- [c++] 函数指针和函数指针数组的使用方法
- c++函数参数类型-值,指针,引用
- C++函数参数和返回值三种传递方式:值传递、指针传递和引用传递(着重理解)
- C/C++中指针和引用之相关问题研究
- Boost.Bind用法详解(一) 2008-05-09 15:50:50| 分类: C++ |字号 订阅 Boost.Bind 为函数和函数对象提供了一致的语法,对于值语义和指针语义也一样。
- C++ 函数内部定义函数原型?