关闭

C++ Primer 类 12.3-12.5 作用域,construtor,friend

标签: c++
123人阅读 评论(0) 收藏 举报
分类:

12.3 类作用域

1.在定义于类外部的成员函数中,形参表和成员函数体都出现在成员名之后。这些都是在类作用域中定义,所以可以不用限定而引用其他成员。例如,类 Screen中 get 的二形参版本的定义:

 

     char Screen::get(index r, index c) const
     {
         index row = r * width;      // compute the row location
 
         return contents[row + c];   // offset by c to fetch specified character
     }

该函数用 Screen 内定义的 index 类型来指定其形参类型。因为形参表是在 Screen 类的作用域内,所以不必指明我们想要的是 Screen::index。我们想要的是定义在当前类作用域中的,这是隐含的。同样,使用 index、width 和 contents 时指的都是 Screen 类中声明的名字。

2.函数返回类型不一定在类作用域中。必须用完全限定的类型名 Screen::index 来指定所需要的 index 是在类 Screen 中定义的名字。

3.类作用域中的名字查找 。(看书,最好的办法是取不同的名字)

12.4 构造函数

※构造函数是特殊的成员函数。在类对象定义时被调用。 不能通过定义的类对象调用构造函数,构造函数可以定义多个或者说构造函数允许重载。

※如果没有定义任何构造函数,系统就会给类分配一个无参的默认构造函数,类只要定义了一个构造函数,编译器也不会再生成默认构造函数。只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数.

※定义类对象时不能写成 Sales_item myobj(); 编译器会理解成:一个返回 Sales_item 类型叫 myobj的函数声明。 正确写法是去掉后面的括号。

※构造函数后面不允许定义成 const,这样定义会产生语法错误: Sales_item() const {};

※构造函数在执行时会做类数据成员的初始化工作。从概念上讲,可以认为构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。

※不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员(比如string)总是在初始化阶段初始化。初始化发生在计算阶段开始之前。

※使用构造函数初始化列表的版本初始化数据成员,没有定义初始化列表的构造函数版本在构造函数函数体中对数据成员赋值。

※记住,可以初始化 const 对象或引用类型的对象,但不能对它们赋值。在开始执行构造函数的函数体之前,要完成初始化。初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中。

※构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序

※具有默认实参的构造函数都会被编译器认为是默认构造函数,而默认构造函数只能有一个。

※http://write.blog.csdn.net/postedit/51541387

    ※下面的陈述中哪个是不正确的(如果有的话)?为什么?

    (a)类必须提供至少一个构造函数。

    (b)默认构造函数的形参列表中没有形参。

    (c)如果一个类没有有意义的默认值,则该类不应该提供默认构造函数。

    (d)如果一个类没有定义默认构造函数。则编译器会自动生成一个,同时将每个数据成员初始化

为相关类型的默认值。

【解答】

    (a)不正确。因为类也可以不提供构造函数,这时使用由编译器合成的默认构造函数。

    (b) 不正确。因为为所有形参都提供了默认实参的构造函数也定义了默认构造函数,而这样的构造函数形参列表中是有形参的。

    (c)不正确。囚为如果一个类没有默认构造函数(指的是该类提供了构造函数,但没有提供自己的默认构造函数),则在编译器需要隐式使用默认构造函数的环境中,该类就不能使用,所以,如果一个类定义了其他构造函数,则通常也应该提供1个默认构造函数(见12.4.3节)。

    (d)不正确。因为编译器合成的默认构造函数,不是将侮个数据成员初始化为相关类型的默认值,而是使用与变量初始化相同的规则宋初始化成员:类类型的成员执行各自的默认构造函数进行初始化;内置和复合类型的成员,只对定义在全局作用域中的对象才初始化(见12.4.3节)。

    ※默认情况下可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。

class mycls
{
    public:
        int i;

        mycls(int i){ };
 
        explicit mycls(string s){ };

};


mycls obj(2) ; 也可以这样使用这个构造函数 mycls obj = 2; 这里做了一个类型转换,但是这样的写法很不直观。

可以通过将构造函数声明为 explicit,来防止在需要隐式转换的上下文中使用构造函数:mycls obj("tom"), 无法用 mycls obj = "tom" 因为转换被禁止,通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit。     

※explicit 关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的定义上不再重复它。

12.5 友元

    ※在需要允许某些特定的非成员函数访问一个类的私有成员〔及受保护成贝)。而同时仍阻止一般的访问的情况下,友元是有用的。

    ※使用友元的优点:可以灵活地实现需要访问若干类的私有或受保护成员才能完成的任务;便于与其他不支持类概念的语言〔如C语言、汇编语言等)进行混合编程:通过使旧友元函数重载可以更自然地使用C一语言的I/O流库。

    ※使用友元的缺点:,个类将对其非公有成员的访问权授子其他的函数或类,会破坏该类的封装性,降低该类的可靠性和可维护性。

※友元的关系是单向的而不是双向的。如果声明了 B类是A类的友元类,不等于A类是B类的友元类,A类中的成员函数不能访问B类中的私有数据。

※友元的关系不能传递,如果B类是A类的友元类,C类是B类的友元类,不等于 C类是A类的友元类。

#include <iostream>
using namespace std;
 
class Bezaa; //Forward declaration of class Bezaa in order for example to compile.
class Aazaa
{
private:
    int a;
public:
    Aazaa() { a = 0; }
    void show(Aazaa& x, Bezaa& y);
    friend void show(Aazaa& x, Bezaa& y);// declaration of global friend
};
 
class Bezaa
{
private:
    int b;
public:
 
    Bezaa() { b = 6; }
    friend void show(Aazaa& x, Bezaa& y);// declaration of global friend
    friend void Aazaa::show(Aazaa& x, Bezaa&y); // declaration of friend from other class
};
 
// Definition of amember function of Aazaa; this member is a friend of Bezaa
void Aazaa::show(Aazaa&x, Bezaa& y)
{
  cout << "Show via function memberof Aazaa" << endl;
  cout << "Aazaa::a = "<< x.a << endl;
  cout << "Bezaa::b = "<< y.b << endl;
}
 
// Friend for Aazaaand Bezaa, definition of global function
void show(Aazaa& x,Bezaa& y)
{
  cout << "Show via globalfunction" << endl;
  cout << "Aazaa::a = "<< x.a << endl;
  cout << "Bezaa::b = "<< y.b << endl;
}
 
int main()
{
   Aazaa a;
   Bezaa b;
 
   show(a,b);
   a.show(a,b);
   system("pause");
}


输出结果为:

 

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:44727次
    • 积分:850
    • 等级:
    • 排名:千里之外
    • 原创:27篇
    • 转载:95篇
    • 译文:0篇
    • 评论:2条
    最新评论