1.在定义一个类之前可以声明,class Screen;这个声明就是前向声明,在程序中引入了类类型的Screen,在声明之后定义之前,Screen是一个不完全类型,即已知Screen是一个类型,但不知道包含哪些成员。我们知道定义一个类的对象会为其分配内存空间,而当Screen为不完全类型的时候,显然是不能定义Screen对象的,除此之外不完全类型只能定义指向该类的指针或者引用,或者用于声明(不是定义)使用该类型作为形参类型或者返回类型的函数。因为不完全类型,所以类的数据成员可以是指向自身的指针或者引用,如:
class a
{
a *next;
a *prev;
}.
定义一个类对象有两种方式,
a.将类的名字直接作为类型名 :a tmp;C++语言引入,使得类类型更容易使用;
b.指定关键字class或struct,后面跟类的名字:class a tmp继承与C语言,C++中仍然有效。
2.this指针与const:在普通的非const成员函数中,this的类型是一个指向类类型的const指针(可以改变this指向的值,但不能改变this所保存的地址),在const成员函数中,this是一个指向const类类型对象的const指针(不能改变this指向的值,也不能改变this所保存的地址)注意:不能从const成员函数返回指向类对象的普通引用,只能返回*this作为一个const引用。
-->基于const的重载:
Screen myScreen;
myScreen.display().set('*');
如果display返回的是const引用(打印的话不会改变对象,所以应该是一个const成员,返回的将是一个const Screen&--因为const成员只能返回*this作为一个const引用),上面的表达式将是非法,因为不能在const对象上调用非const成员函数,即set。在保留display可能为const特性的情况下,可以重载display,一个是const,一个不是const用来解决上面的问题(原理:const对象只能使用const成员,非const对象可以使用任一成员,但非const版本是一个更好的匹配)。
此时的Sreen::index必须全写。
4.构造函数不能声明为const,因为构造函数的功能就是初始化(注意初始化列表和构造函数体内赋值的区别,一个是初始化,一个是赋值)类的数据成员。构造函数可以被声明为private,protected。在声明为private时,外部是无法定义这个类的对象(A a;error),但是可以在成员函数中定义这个类对象,当然只有在这个函数为static时才有价值。还有一个情况是:通常将拷贝构造函数和operator=(赋值操作符重载)声明为private,但是没有实现体。这个的目的是禁止一个类的外部用户对这个类的对象进行复制动作。
5.对于构造函数而言,使用初始化列表和在构造函数内对成员赋值的区别在于:前者是初始化而后者仅仅是赋值(也就是说在构造函数内赋值的时候,其实数据成员已经有值了)。如果数据成员为类类型且没有为其提供显式初始化式时,编译器隐式调用其默认构造函数初始化,所以在这个类没有默认构造函数的情况下,必须提供初始化式。而const或引用类型成员必须在定义的时候初始化,所以此时也必须提供初始化式。结论:必须对任何const或引用类型成员以及没有默认构造函数的类类型成员使用初始化式。注意:成员初始化的次序就是定义成员的次序。
6.默认实参与构造函数:
class A
{
public:
A()
{
cout<<" A construct "<<endl;
}
A(int a = 0)
{
cout<<" A construct ====a"<<endl;
}
};
A a;//调用哪个构造函数呢?两个构造函数都可以,产生冲突,编译不过(当然,你不定义对象的话是可以编译通过的),类似的使用默认实参时要注意可能产生的问题(注意普通函数也一样有这个问题)。
7. 使用默认构造函数:
A a; 定义一个A类型的对象a
A a(); 声明一个函数,无参数,返回值类型为A
A a = A(); 先调用默认构造函数创建一个A类型的临时对象,然后调用复制构造函数使用该对象初始化a;
8.隐式类类型转换
编译器使用接受一个string的Sales_item的构造函数从null_book生成一个新的Sales_item对象,新生成的(临时的)Sales_item被传递给same_isbn,即将string隐式转换为Sales_item。
item.same_isbn(cin),这里同样将cin隐式转换为Sales_item,然后传递给same_isbn。
之所以能发生上面的隐式转换,是因为有接受参数为string和istream类型的构造函数。能不能隐式转换取决于是否有接受相应类型参数的构造函数。
上面两段代码隐式转换生成的Sales_item对象是临时对象,一旦same_isbn结束,就不能再访问,即构造了一个测试完成后被丢弃的对象,这个行为肯定是错误的。
可以通过将构造函数声明为explicit来避免隐式转换(explicit只能用于类内部构造函数声明上,类外部构造函数的定义可以不再重复),当构造函数声明为explicit后,编译器将不使用它作为转换操作符,上面的代码将是非法的。如果一定要转换,可以显式的使用构造函数来转换,比如:
string null_book = "9-999-99999-9";
item.same_isbn(Sales_item(null_book));
结论:通常除非有明显的理由想要定义隐式转换,否则,单形参的构造函数应该声明为explicit,这样可以避免错误,想要转换时,可以显示地调用构造函数来实现转换。
9.友元可以是普通的非成员函数,或者前面定义的其他类的成员函数,或者整个类,声明为某个类的友元后就可以在友元中访问这个类的所有成员包括private和protected成员。
10.static成员不是类对象的组成部分,所以:static成员函数不能访问非static成员;static没有this指针;static成员函数不能声明为const,毕竟声明为const就是承诺不会修改该函数所属的对象,而static不属于任何对象;static成员函数也不能声明为虚函数。
static数据成员必须在类定义体的外部定义,普通数据成员通过构造函数初始化,而static成员应该在定义的时候初始化(有一个例外:整型的const static数据成员可以在类的定义体中进行初始化,前提是初始化式是常量表达,当然该数据成员仍然必须在类的定义体之外定义)。
注意:
public Bar
{
public:
....
private:
static Bar mem1;//ok
Bar *mem2;//ok
Bar men3;//error
}
前向声明,非static成员只能被限定声明为其自身类对象的指针或引用,而static成员则可以声明该类的对象。