对于头文件不应该有定义有三个例外(需要定义来产生代码而不是声明):头文件可以定义类、编译时就知道值的const对象和inline函数。定义类是因为产生对象需要完整类类型定义;知道值const对象是因为用const定义全局变量默认是static的,即只能被定义该const对象的文件使用,因此不会出现多重定义问题;定义inline是因为编译时候函数要展开。
(3) 常量表达式是指编译器在编译时就能计算出结果的表达式,C++常量表达式有枚举量、宏常量、用常量表达式初始化的const变量,如:
const int n; //不是常量表达式,c++中会报错要求const对象必须初始化,或是 extern
const int n = 10; //常量表达式初始化的const变量,是常量表达式,编译时知道值
const int a = b; //变量初始化的const变量,不是常量表达式,编译时不知道值
c++中定于数组时候就要求数组大小是常量表达式,即编译时候知道数组大小分配内存
int a[n] C++编译器采用指针来实现引用,引用呵指针的3大区别:
1. 引用不能为空
2. 所有引用必须初始化
3. 引用整个生命期只能绑定一个对象
(5) 一个类在定义前使用可以采用前向声明,但前向声明的类是一个不完整类类型,只能以受限的方式使用,不能定义对象,只能用于定于该类型的指针或引用或是使用使用该类型作为形参或返回类型的函数声
class A;
class B
{
public:
private:
A *m_a;
};
int Fun(A a);
(6) 类的inline成员函数,inline关键字 声明时可有可无 定义时指定
类的const成员函数,const关键字 声明时指定 定义时指定
类的virtual成员函数,virtual关键字 声明时候指定,定义时可有可无
类的带默认参数成员函数 默认参数 声明时指定, 定义时可有可无
(7) 构造函数由编译器自动调用完成对象的初始化。可以理解为两个阶段:初始化阶段和构造函数体执行阶段。
1.初始化阶段:默认对所有数据成员初始化,自定义类型调用默认构造函数,内置和复合类型初值取决于作用域,全局作用域和static局部变量会初始化为0。也可以自己定义初始化列表,来指定初始化规则。
2.构造函数体执行阶段:这个阶段所有成员已经初始化过了, 可以做一些赋值操作。
因此,一般情况下,在两个阶段都可以对成员初始化,但对于对象成员由于构造函数的开销,所以构造函数中一般:
3.对象成员用初始化列表
4.内置类型用函数体赋值(和初始化列表效率一样,但更直观)
以下情况只能用初始化列表而不能在函数体内赋值
1.const数据成员
2.引用成员
3.成员对象所属的类没有默认构造函数(默认的初始化工作不了,必须显示指定了)
(8) C++中explicit关键字用于禁止隐式转换,类的每个构造函数都定义了从构造函数参数类类型到参数的隐式转换。
-
class A
-
{
-
public:
-
A(int a){} //use explicit befor declaration of ctor can avoid implicit conversion
-
A(string s){}
-
};
-
-
void fun(A a)
-
{
-
cout<<"in fun"<<endl;
-
}
-
-
int main()
-
{
-
string str("hello");
-
fun(1); // implicit conversion from int to A, Create a temp A object
-
fun(str); // implicit conversion from string to A, Create a temp A object
-
system("pause");
-
}
(9) 如果需要禁止一个类复制,必须显示将复制构造函数声明为private,复制构造函数私有不允许用户代码复制该类类型的对象。然后友元和成员函数仍然可以进行复制。如果连友元和成员函数函数都要禁止复制,则需要声明一private复制构造函数,但不定义,这样可以实现完全禁止类的复制。
-
class A
-
{
-
public:
-
A()
-
{
-
cout<<"A::A()"<<endl;
-
}
-
void call_copy()
-
{
-
A b1;
-
A b2(b1); // call copy ctor, link error
-
}
-
-
private:
-
A(const A &rhs); //just a declaration
-
-
private:
-
int m_i;
-
};
-
-
int main()
-
{
-
A a;
-
A a1(a); //call copy ctor, complier error
-
system("pause");
-
}
对于赋值运算符的禁止类似,下面是用于让类禁止复制或赋值的宏
-
/* --------------------------------------------------------------------------- */
-
/* macro to define a class without copy ctor nor assignment operator */
-
/* --------------------------------------------------------------------------- */
-
-
#define DECLARE_NO_COPY_CLASS(classname) \
-
private: \
-
classname(const classname&); \
-
classname& operator=(const classname&);
-
-
#define DECLARE_NO_ASSIGN_CLASS(classname) \
-
private: \
-
classname& operator=(const classname&);
(10) 每个类都有构造函数、析构函数、赋值运算符函数(号称Big three), 如果没有编译器会合成一个。例如对于类foo
1.如果没有定义普通构造函数foo(),会合成一个合成构造函数foo(),这个函数啥也不干。
2。如果没有定义复制构造函数foo(const foo &rhs),会合成一个合成复制构造函数,foo(const foo &rhs), 这个函数会对非static成员位拷贝
3.如果没有赋值运算符重载函数 foo & operator =(const foo &rhs),会合成一个合成赋值运算符重载函数,foo(const foo &rhs), 这个函数会对非static成员位拷贝
4.不管有没有析构函数~foo(),编译器都会合成一个析构函数,按照逆序析构非static成员。然后再执行定义的析构函数的函数体操作