最近在翻阅EffectiveC++一书,就边学边做笔记了,之前很多东西没能及时整理上来,当时的想法是害怕自己在阅读过程中很多东西不能够理解的很深刻,之后就是不断的遗忘再遗忘或者随着时间的推移就不想去码字了,时间真是个可怕的东西,年龄大了就开始害怕了。
C++术语(Terminology)
1.声明式(declaration)
告诉编译器某个东西的名称和类型(type),但略去细节。
for example
extern int x; //对象(object)声明式
std::size_t numDigits(int number); //函数(function)声明式
class Widget; //类(class)声明式
template<typename T> //模板(template)声明式
class GraphNone; //"typename"的使用见条款42
2.签名式(signature)
也就是参数和返回类型。一个函数的签名等同于该函数的类型。
numDigits函数的签名是std::size_t (int),也就是说“这函数获得一个int并返回一个std::size_t”。
3.定义式(definition)
定义式的任务是提供编译器一些声明式所遗漏的细节。
对对象而言,定义式是编译器为此对象拨发内存的地点。
对function或function template而言,定义式提供了代码本体。
对class 或 class template 而言,定义式列出它们的成员。
int x; //对象的定义式
std::size_t numDigits(int number) //函数的定义式
{ //此函数返回参数的数字个数
std::size_t digitsSoFar = 1; //例如十位数返回2,百位数返回3
while ((number /= 10) != 0) ++digitsSoFar;
return digitsSoFar;
}
class Widget{ //class定义式
public:
Widget();
~widget();
...
};
template<typename T> //template的定义式
class GraphNode {
public:
GraphNode();
~GraphNode();
...
};
4.初始化(initialization)
是“给予对象初始值”的过程。对用户自定义类型的对象而言,初始化由构造函数执行。所谓default构造函数是一个可被调用而不带任何实参者。这样的构造函数要不没有参数,要不就是每个参数都有缺省值:
class A{
public:
A(); //default构造函数
};
class B{
public:
explicit B(int x = 0, bool b = true); //default构造函数
};
class C{
public:
explicit C(int x); //不是default构造函数
}
5.explicit关键字和隐式类型转换(implicit type conversions)
一篇博客讲的比书上详细特摘录如下:(原文地址点击打开链接)
//----------------------------------------------------------------------------------------------------------------
for example: 1.1
#include <string>
#include <iostream>
using namespace std;
class Fruit //定义一个类,名字叫Fruit
{
string name; //定义一个name成员
string colour; //定义一个colour成员
public:
bool isSame(const Fruit &otherFruit) //期待的形参是另一个Fruit类对象,测试是否同名
{
return name == otherFruit.name;
}
void print() //定义一个输出名字的成员print()
{
cout<<colour<<" "<<name<<endl;
}
Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst){} //构造函数
Fruit(){}
};
int main()
{
Fruit apple("apple");
Fruit orange("orange");
cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl; //没有问题,肯定不同
cout<<"apple = /"apple/" ?:"<<apple.isSame(string("apple")); //用一个string做形参?
return 0;
}
你会发现最后的使用上,我们用一个string类型作一个期待Fruit类形参的函数的参数,结果竟然得出了是true(1),不要感到奇怪,这就是我现在要讲的东西,隐式类类型转换:“可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。”(C++ Primer)首先要单个实参,你可以把构造函数color的默认实参去掉,也就是定义一个对象必须要两个参数的时候,文件编译不能通过。然后满足这个条件后,系统就知道怎么转换了,不过这里比较严格:)以前我们构造对象的时候Fruit apple("apple")其实也已经有了一个转换,从const char *的C字符串格式,转为string,在这里,你再apple.isSame("apple")的话,蠢系统不懂得帮你转换两次,所以你必须要用string()来先强制转换,然后系统才知道帮你从string隐式转换为Fruit,当然其实你自己也可以帮他完成。cout<<"apple = /"apple/" ?:"<<apple.isSame(Fruit("apple"));这样。参考例子1.2 :Fruit apple = Fruit("apple"); //定义一个Fruit类对象apple。也就是这样转换的。不过这就叫显式转换了,我们不标出来,系统帮我们完成的,叫隐式的呗。这里要说的是,假如你显示转换就可以不管有多少参数了,比如在前面提到的必须需要两个参数的构造函数时的例子。
1.2
#include <string>
#include <iostream>
using namespace std;
class Fruit //定义一个类,名字叫Fruit
{
string name; //定义一个name成员
string colour; //定义一个colour成员
public:
bool isSame(const Fruit &otherFruit) //期待的形参是另一个Fruit类对象,测试是否同名
{
return name == otherFruit.name;
}
void print() //定义一个输出名字的成员print()
{
cout<<colour<<" "<<name<<endl;
}
Fruit(const string &nst,const string &cst):name(nst),colour(cst){} //构造函数
Fruit(){}
};
int main()
{
Fruit apple("apple","green");
Fruit orange("orange","yellow");
cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl; //没有问题,肯定不同
cout<<"apple = /"apple/" ?:"<<apple.isSame(Fruit("apple","green")); //显式转换
return 0;
}
在你不想隐式转换,以防用户误操作怎么办?C++提供了一种抑制构造函数隐式转换的办法,就是在构造函数前面加explicit关键字,你试试就知道,那时你再希望隐式转换就会导致编译失败,但是,要说明的是,显式转换还是可以进行,出于不提供错误源代码例子的原则,错误的情况就不提供了,自己试试吧:)在说这个东西之前,我还不懂,现在我懂了:)我现在好像都习惯边学边讲了,有什么错误,你可要指出来啊。
//------------------------------------------------------------------------------------------------------------
被声明为explicit的构造函数通常比其non-explicit 兄弟更受欢迎,因为它们禁止允许编译器执行非预期(往往也不被期望)的类型转换。
好的,到此结束了!