`
shi5jin
  • 浏览: 37188 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

为何我的构造函数不太对劲?

阅读更多

 类似这样的问题千奇百怪。例如:

    • 为什么我明明不想复制对象,而编译器却偏偏这么做了呢?
    • 如何关闭复制机制?
    • 如何防止隐式转换?
    • 为何 int 自动转换成了复数?
    类的默认复制构造函数和赋值运算符可以复制所有元素。例如:

        struct Point {
            int x,y;
            Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
        };

        Point p1(1,2);
        Point p2 = p1;

    至此,p2.x==p1.x 并且 p2.y==p1.y。这可能正是你想要的(而且也是为了和 C 兼容所必需的),但是,以下代码:

        class Handle {
        private:
            string name;
            X* p;
        public:
            Handle(string n)
                :name(n), p(0) { /* acquire X called "name" and let p point to it */ }
            ~Handle() { delete p; /* release X called "name" */ }
            // ...
        };

        void f(const string& hh)
        {
             Handle h1(hh);
             Handle h1 = h2; // 会引起灾难!
             // ...
        }

    在此,默认复制构造函数使得 h2.name==h1.name 并且 h2.p==h2.p。这将导致一场灾难:当函数 f() 运行结束时,会调用 h1 和 h2 的析构函数,这就导致 h1.p 和 h2.p 所指向的对象被 delete 了两次。

    如何避免这场灾难?最简单的办法是,将复制构造函数和赋值运算符声明为私有成员,从而关闭复制机制:

        class Handle {
        private:
            string name;
            X* p;

            Handle(const Handle&); // 阻止复制
            Handle& operator=(const Handle&);
        public:
            Handle(string n)
               :name(n), p(0) { /* acquire the X called "name" and let p point to it */ }
            ~Handle() { delete p; /* release X called "name" */ }
             // ...
        };

        void f(const string& hh)
        {
            Handle h1(hh);
            Handle h1 = h2; // 编译器报错
            // ...
        }

    如果需要复制机制,我们可以定义自己的复制构造函数和赋值运算符,让它们按我们期待的那样工作。

    现在回过头来再看看类 Point。对 Point 来说,可以使用默认的复制机制,但它的构造函数有点问题:

        struct Point {
            int x,y;
            Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
        };

        void f(Point);

        void g()
        {
            Point orig;  // 使用默认值 (0,0) 创建 orig 
            Point p1(2); // 使用 yy 的默认值 (0) 来创建 p1
            f(2);        // 调用 Point(2,0);
        }

    为了便于创建对象(如这里的 orig 和 p1),我们为 Point 的构造函数提供了默认参数。然后,有些人会感到惊讶的事情发生了:调用 f() 时,2 会转换成 Point(2,0)。当我们定义一个接受单个参数的构造函数时,同时亦定义了一种类型转换方式。默认情况下,类型转换是隐式进行的。若想把类型转换改成 显式进行,就要将构造函数声明为 explicit:

        struct Point {
            int x,y;
            explicit Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
        };

        void f(Point);

        void g()
        {
            Point orig;   // 使用默认值 (0,0) 创建 orig 
            Point p1(2);  // 使用 yy 的默认值 (0) 来创建 p1
                          // 显式调用构造函数
            f(2);         // 错误(试图进行隐式转换)
            Point p2 = 2; // 错误(试图进行隐式转换)
            Pont p3 = Point(2); // 正确(显式转换)
        }

原文地址:http://www.research.att.com/~bs/bs_faq2.html#explicit-ctor

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics