综述
在c++中声明与定义是如此相似却又不尽相同。以至于在很多地方会把声明与定义混淆。有关声明与定义的讨论总是离不开翻译单元,每个翻译单元都可有编译器独立编译,因而翻译单元必定包含了它所需要用到的所有变量和函数描述符。对翻译单元而言,若描述符有完整的实现,则称其为定义了该描述符,否则称之为声明了该描述符。
异同
- 在程序中,声明可以发生多次但定义只能有一次。
- 编译器不会为声明分配内存(确定内存大小),而定义则相反。如全局变量或静态变量都会在编译时分配内存。
- 另外定义类和定义名称空间不一样
常见声明
- 类定义(不定义的话不知道需要为该类型分配多大的内存)
- 带有static的内部链接变量
- enum
- 函数的原型可以出现多次,但是定义只能出现一次
声明和定义的区分
- 从编译器是否分配内存出发
- 从静态变量的链接性出发
类中的成员变量
一般情况下,类定义只是告诉编译器如何为此类型分配内存而不马上分配内存,因此在类定义中初始化成员变量并不可行,可是也还有例外。
class ABC {
public:
int value = 0; // 不允许 不分配内存 (c++11 允许,但在实例化对象时初始化:https://en.cppreference.com/w/cpp/language/data_members)
static int value = 0; // 不允许 不分配内存
const int value = 0; // 不允许 不分配内存(c++ 11 允许,但在实例化对象时初始化)
const static int value = 0; // 允许 由于是const,编译器会进行优化,直接把变量名替换,因此对于翻译单元来说,变量名并不存在。这是声明但不是定义。
}
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
}
作用域与存储类型
作用域是指,从变量的声明开始,到其退出被销毁之间的一段空间。可分为全局和局部两种。
按照变量的作用域,可以将变量划分为静态、动态和自动三种存储类型。详细描述如下:
- 静态变量:作用域为全局或者用static修饰的变量,在程序的运行期间一直存在。
- 动态变量:通过new关键字分配的变量,需有调用delete 来回收响应内存空间。
- 自动变量:作用域为函数内或者代码块内,离开作用域后被自动回收
例子
- 内联函数的作用域在一个翻译单元内
- 静态存储类型的变量在声明时会被初始化为0值
- using namespace 会传递,例子如下:
// namespace1.cpp
#include <iostream>
namespace test1 {
using namespace std;
void TestFunction(int n) {
cout << n << endl;
}
}
// namespace2.cpp
#include "namespace1.cpp"
using namespace test1;
int main() {
cout << "abc" << endl;
}
// namespace1中的using namespace std 通过test1传递到了namespace2中
- using会使得namespace中的成员引入到全局变量中,因为namespace被#include引入到全局变量:https://blog.youkuaiyun.com/ljq32/article/details/7950629 该特性可能会导致变量产生二异性。