现在的位置: 首页 > 综合 > 正文

[C++ Primer] 类

2018年04月03日 ⁄ 综合 ⁄ 共 3150字 ⁄ 字号 评论关闭

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版本是一个更好的匹配)。


 
当将display嵌入到一个超表达式中,将调用非const版本,而当display一个const对象时,将调用const版本。-----注意不是根据参数来重载
3.Sreen类定义中有这样一句:typedef std::string::size_type indx;
形参表和函数体处于类作用域中,所以下面的代码是合法的:

 
Sreen::index可以写成index,而函数返回类型不一定在类作用域中,如果函数在类定义体外定义,则函数返回类型在类作用域外,必须使用完全限定名。
 
 
 

此时的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成员则可以声明该类的对象。

抱歉!评论已关闭.