C++ 为什么类的私有构造函数捕获模式可以访问类的私有成员

随笔- 395&
评论- 156&
&&&&&&&&&&&&&
  很多情况下要求当前的程序中只有一个object。例如一个程序只有一个和数据库的连接,只有一个鼠标的object。通常我们都将构造函数的声明置于public区段,假如我们将
其放入private区段中会发生什么样的后果?这意味着什么?  当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,
由于在class外部不允许访问私有成员,所以这将导致编译出错。
  然而,对于class本身,可以利用它的static公有成员,因为它们独立于class对象之外,不必产生对象也可以使用它们。
  此时因为构造函数被class私有化,所以我们要创建出对象,就必须能够访问到class的私有域;这一点只有class的成员可以做得到;但在我们建构
出其对象之前,怎么能利用它的成员呢?static公有成员,它是独立于class对象而存在的,&我们&可以访问得到。假如在某个static函数中创
建了该class的对象,并以引用或者指针的形式将其返回(这里不以对象返回,主要是构造函数是私有的,外部不能创建临时对象),就获得了这个对象的使用权。
  下面是例子:
class OnlyHeapClass
static OnlyHeapClass* GetInstance()
// 创建一个OnlyHeapClass对象并返回其指针
return (new OnlyHeapClass);
void Destroy();
OnlyHeapClass() { }
~OnlyHeapClass() {}
int main()
OnlyHeapClass *p = OnlyHeapClass::GetInstance();
... // 使用*p
  这个例子使用了私有构造函数,GetInstance()作为OnlyHeapClass的静态成员函数来在内存中创建对象:由于要跨函数传递并且不能使用值传递方式,所以我们选择在堆上
创建对象,这样即使getInstance()退出,对象也不会随之释放,可以手动释放。
  构造函数私有化的类的设计保证了其他类不能从这个类派生或者创建类的实例,还有这样的用途:例如,实现这样一个class:它在内存中至多存在一个,或者指定数量个
的对象(可以在class的私有域中添加一个static类型的计数器,它的初值置为0,然后在GetInstance()中作些限制:每次调用它时先检查计数器的值是否已经达到对象个数的
上限值,如果是则产生错误,否则才new出新的对象,同时将计数器的值增1.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别
声明并置为私有。
  如果将构造函数设计成Protected,也可以实现同样的目的,但是可以被继承。
  另外如何保证只能在堆上new一个新的类对象呢?只需把析构函数定义为私有成员。
  原因是C++是一个静态绑定的语言。在编译过程中,所有的非虚函数调用都必须分析完成。即使是虚函数,也需检查可访问性。因些,当在栈上生成对象时,对象
会自动析构,也就说析构函数必须可以访问。而堆上生成对象,由于析构时机由程序员控制,所以不一定需要析构函数。保证了不能在栈上生成对象后,需要证明能
在堆上生成它。这里OnlyHeapClass与一般对象唯一的区别在于它的析构函数为私有。delete操作会调用析构函数。所以不能编译。
  那么如何释放它呢?答案也很简单,提供一个成员函数,完成delete操作。在成员函数中,析构函数是可以访问的。当然detele操作也是可以编译通过。
void OnlyHeapClass::Destroy() {
delete this;
  构造函数私有化的类的设计可以保证只能用new命令在堆中来生成对象,只能动态的去创建对象,这样可以自由的控制对象的生命周期。但是,这样的类需要提供创建和撤销
的公共接口。
  另外重载delete,new为私有可以达到要求对象创建于栈上的目的,用placement new也可以创建在栈上。
还是不懂啊: &&
& 1.为什么要自己调用呢?对象结束生存期时不就自动调用析构函数了吗?什么情况下需要自己调用析构函数呢? &&
& ================================================================ &&
& 比如这样一种情况,你希望在析构之前必须做一些事情,但是用你类的人并不知道,那么你就可以重新写一个函数,里面把要做的事情全部做完了再调用析构函数。这样人家只能调用你这个函数析构对象,从而保证了析构前一定会做你要求的动作。 &&
& 2.什么情况下才用得着只生成堆对象呢? &&
& ================================ &&
& 堆对象就是new出来的,相对于栈对象而言。什么情况下要new,什么情况下在栈里面提前分配,无非就是何时该用动态,何时该用静态生成的问题。这个要根据具体情况 &&
具体分析。比如你在一个函数里面事先知道某个对象最多只可能10个,那么你就可以定义这个对象的一个数组。10个元素,每个元素都是一个栈对象。如果你无
法确定数字,那么你就可以定义一个这个对象的指针,需要创建的时候就new出来,并且用list或者vector管理起来。&&&
---------------------------------------------------------------------------------------------------------------------------------
类中&私有&权限的含义就是:私有成员只能在类域内被访问,不能在类域外进行访问。 &&
& 把析构函数定义为私有的,就阻止了用户在类域外对析构函数的使用。这表现在如下两个方面: &&
& 1.禁止用户对此类型的变量进行定义,即禁止在栈内存空间内创建此类型的对象。要创建对象,只能用new在堆上进行。 &&
& 2.禁止用户在程序中使用delete删除此类型对象。对象的删除只能在类内实现,也就是说只有类的实现者才有可能实现对对象的delete,用户不能随便删除对象。如果用户想删除对象的话,只能按照类的实现者提供的方法进行。 &&
& 可见,这样做之后大大限制了用户对此类的使用。一般来说不要这样做;通常这样做是用来达到特殊的目的,比如在singleton的实现上。
from:http://blog.csdn.net/koudaidai/article/details/7546661
&posted on
阅读(...) 评论()学习笔记-----关于C++中类的成员函数可以访问私有成员的问题 - 一个程序渣渣的小后院 - CSDN博客
学习笔记-----关于C++中类的成员函数可以访问私有成员的问题
直接上代码吧
template&class T&
class vectorList : public linearList&T&
vectorList(size_t initCapacity = 10);
vectorList(const vectorList&T& & rhs);
~vectorList();
protected:
void checkIndex(size_t theIndex);
size_t vectorL
size_t listS
大致就是这样,主要考虑拷贝构造函数vectorList(const vectorList&T& & rhs);该函数的定义如下:
template&class T&
vectorList&T&::vectorList(const vectorList&T&& rhs) :
element(vector&T&(rhs.vectorLength)),
vectorLength(rhs.vectorLength),
listSize(rhs.listSize)
copy(rhs.element.begin(), rhs.element.end(), element);
显然,在这个成员函数中rhs直接访问了自身的私有成员vectorLength和listSize
而如果在main.cpp里也这样调用的话就是错的
vectorList&int& vec(2);
cout && vec.vectorLength &&
错误:编译器会提示没有访问权限
查了一些资料发现
1:与C++机制有关,是C++特殊规定的操作,因为类是设计者自己设计的,设计者完全知道类内部的组成,不需要对设计者隐藏。而使用者即用户不知道类的内部结构,所以分出Public,protected,和private.而且不能再main中调用
2:访问限定符public,protected,private是对其他的类和其他的操作而言的,即如果两个对象属于一个类:上述rhs和*this,在其中一个对象的成员函数中是可以直接调用另一个对象的私有或者保护成员的
3:因为类的接口与封装已经是安全的(对外不可见的),所以在类内部这样使用是不存在不安全问题的
4:如果没有这一机制,则上述拷贝构造函数的初始化就需要在类内增加一些成员函数来返回私有或者保护成员。
我的热门文章c++类构造函数详解
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了c++类构造函数示例,需要的朋友可以参考下
代码如下://一、 构造函数是干什么的
/*&& 类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数-&由构造函数完成成员的初始化工作&&&& eg: Counter c1;
&&&& 编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象,初始化之后c1的m_value值设置为0
&&&& 故:构造函数的作用:初始化对象的数据成员。*/
&&&&& class Counter&&&&&& {&&&&& public:&&&&&& // 类Counter的构造函数,以类名作为函数名,无返回类型&&&&& Counter(){ &&&&& m_value = 0;&&&&& }
&&&&& private:&&&&& int m_& // 类私有的数据成员&&&&& }
//二、 构造函数的种类
#include &iostream&
class Complex{private :&&& double m_&&& double m_
//*无参数构造函数
// 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做// 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
&&& Complex(void)&&& {&& m_real = 0.0;&&&&&&& m_imag = 0.0;&&& }
//*一般构造函数(也称重载构造函数)
//一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
//例如:你还可以写一个 Complex(int num)的构造函数出来,创建对象时根据传入的参数不同调用不同的构造函数
&&& Complex(double real, double imag)&&& {&& m_real =&&&&&&& m_imag =&&& }
//*复制构造函数(也称为拷贝构造函数)
//复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
//若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因在有关 “浅拷贝”、“深拷贝”的文章中论述
&&& Complex(const Complex & c)&&& {&& // 将对象c中的数据成员值复制过来&&&&&&& m_real = c.m_&&&&&&& m_imag = c.m_&&& }
//*类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象,需要注意的一点是,这个其实就是一般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,
//有时候这种隐私的转换是我们所不想要的,所以需要使用explicit来限制这种转换。
//例如:下面将根据一个double类型的对象创建了一个Complex对象
&&& Complex(double r)&&& {&& m_real =&&&&&&& m_imag = 0.0;&&& }
// 等号运算符重载(也叫赋值构造函数)
// 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建。
// 若没有显示的写 =运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
&&& Complex &operator=(const Complex &rhs )
&&& {&& // 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回&&&&&&& if ( this == &rhs )&&&&&&& {&& return *&&&&&&& }
&&&&&&& // 复制等号右边的成员到左边的对象中&&&&&&& this-&m_real = rhs.m_&&&&&&& this-&m_imag = rhs.m_
&&&&&&& // 把等号左边的对象再次传出,目的是为了支持连等 eg:a=b=c 系统首先运行 b=c 然后运行 a=(b=c的返回值,这里应该是复制c值后的b对象)&&&&&&& return *&&& }};
//三、使用上面定义的类对象来说明各个构造函数的用法:
int main(){&& &&& // 调用了无参构造函数,数据成员初值被赋值为0.0&&& Complex c1,c2;
&&& // 调用一般构造函数,数据成员初值分别被赋为指定值&&& Complex c3(1.0,2.5);
&&& // 当然,也可以使用下面的形式&&& // Complex c3 = Complex(1.0,2.5);
&&& //& 把c3的数据成员的值赋值给事先被创建的对象c1&&& //& 由于c1已经事先被创建,故此处不会调用任何构造函数&&& //& 只会调用 = 号运算符重载函数&&& c1 = c3;&
&&& //& 调用类型转换构造函数&&& //& 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c2&&& c2 = 5.2;&
&&& // 调用拷贝构造函数( 有下面两种调用方式)
&&& Complex c5(c3);&&& Complex c4 = c3;
&&& // 注意和 =运算符重载的区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2&&& // 这一点特别重要,这儿是初始化,不是赋值。&&& // 其实这儿就涉及了C++中的两种初始化的方式:复制初始化和赋值初始化。&&& // 其中c5采用的是复制初始化,而c4采用的是赋值初始化,这两种方式都是要调用拷贝构造函数的。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具20:42 提问
模板类构造函数与析构函数无法访问私有成员(明明就是公有的)
模板类构造函数与析构函数无法访问私有成员(明明就是公有的)
写成这样:
#ifndef __SINGLETON__H__
#define __SINGLETON__H__
template &typename T&
template &typename T&
class Singleton
friend class Worker&T&;
static T* GetInstance()
worker.i=3;
/*worker.m_pInstance = new T();
return worker.m_pI*/
Singleton() {}
~Singleton() {}
/*Singleton(const Singleton&T& &);
Singleton& operator = (const Singleton&T& &);*/
static Worker&T&
template &typename T&
class Worker
friend class Singleton&T&;
if ( !m_pInstance )
{m_pInstance = new T();printf("ccc\n");}
// 出错行在这里
{if ( m_pInstance )
{delete m_pIprintf("ddd\n");}
// 出错行在这里
//template &typename T& T* Singleton&T&::m_pInstance = NULL;
template &typename T& typename Worker&T& Singleton&T&::
#define SINGLETON_INIT(Type) friend Type* Singleton&Type&::GetInstance(); private: Type(); ~Type()
或者是这样,都不行,编译结果一模一样:
#ifndef __SINGLETON__H__
#define __SINGLETON__H__
template &typename T&
template &typename T&
class Singleton
friend class Worker&T&;
static T* GetInstance()
worker.i=3;
/*worker.m_pInstance = new T();
return worker.m_pI*/
Singleton() {}
~Singleton() {}
/*Singleton(const Singleton&T& &);
Singleton& operator = (const Singleton&T& &);*/
static T* m_pI
static Worker&T&
template &typename T&
class Worker
friend class Singleton&T&;
if ( !Singleton&T&::m_pInstance )
{Singleton&T&::m_pInstance = new T();printf("ccc\n");}
// 出错行在这里
{if ( Singleton&T&::m_pInstance )
{delete Singleton&T&::m_pIprintf("ddd\n");}
// 出错行在这里
template &typename T& T* Singleton&T&::m_pInstance = NULL;
template &typename T& typename Worker&T& Singleton&T&::
#define SINGLETON_INIT(Type) friend Type* Singleton&Type&::GetInstance(); private: Type(); ~Type()
编译结果:
A::A”: 无法访问 private 成员(在“A”类中声明)
按时间排序
你怎么编译错误的?main()函数呢?
另外两个文件:
main.cpp:
#include &stdio.h&
#include "head.h"
#include "Singleton.h"
//worker.i = 3;
printf("zzz\n");
printf("mmm\n");
void main()
A* a = NULL;
a = Singleton&A&::GetInstance();
#pragma once
#include "Singleton.h"
class A : public Singleton&A&
SINGLETON_INIT(A);
void Aa(){printf("aaa\n");}
上面的文件改成内部类了,问题一样:
Singleton.h:
#ifndef __SINGLETON__H__
#define __SINGLETON__H__
template &typename T&
class Singleton
friend class W
static T* GetInstance()
worker.i=3;
/*worker.m_pInstance = new T();
return worker.m_pI*/
return NULL;
Singleton() {}
~Singleton() {}
/*Singleton(const Singleton&T& &);
Singleton& operator = (const Singleton&T& &);*/
class Worker
friend class Singleton&T&;
if ( !m_pInstance )
{m_pInstance = new T();printf("ccc\n");}
// 出错行在这里
{if ( m_pInstance )
{delete m_pIprintf("ddd\n");}
// 出错行在这里
//template &typename T& T* Singleton&T&::m_pInstance = NULL;
template &typename T& typename Singleton&T&::Worker Singleton&T&::
#define SINGLETON_INIT(Type) friend Type* Singleton&Type&::GetInstance(); private: Type(); ~Type()
后来发现问题出在:
#define SINGLETON_INIT(Type) friend Type* Singleton::GetInstance(); private: Type(); ~Type()
这句的“private:”里,但我就想问,
两个类都已经相互友元了啊!还没有权限访问私有成员?难道C++在类构造阶段友元是无效的???
Worker类内,改成这样通过:
void aaa()
if ( !m_pInstance )
{m_pInstance = new T();printf("ccc\n");}
void bbb()
if ( m_pInstance )
{delete m_pIprintf("ddd\n");}
改成这样,牵涉到构造析构函数,又不行了:
void aaa()
if ( !m_pInstance )
{m_pInstance = new T();printf("ccc\n");}
void bbb()
if ( m_pInstance )
{delete m_pIprintf("ddd\n");}
两个类都已经相互友元了啊!还没有权限访问私有成员?难道C++在类构造阶段友元是无效的???友元对构造析构函数不起作用?(重要问题说两遍)
互为友元类,可以有权限访问私有成员。但是要注意,私有成员是静态类型,可以直接访问。
如果是非静态类型成员变量,要先用类定义对象,通过对象访问私有数据。否则没定义对象,该成员变量也不存在。
你的设计思路有问题,不知道要干什么用,设计得这么复杂,这么纠结。即使成功,效率也很低,又是模板类,又是继承。
互为友元或者是内部类,然后调用对方的数据,这必然存在一个谁先定义的问题,A先定义,那么B还未定义,在A中就无法使用B。
这个问题很难解决。
其他相关推荐基础备忘:派生类直接访问基类的私有成员
派生类不能直接访问基类的私有成员,若要访问必须使用基类的接口,即通过其成员函数。实现方法有如下两种:
1.在基类的声明中增加保护成员,将基类中提供给派生类访问的私有成员定义为保护成员。
2.将需要访问基类私有成员的派生类成员函数声明为友元。
#include&iostream&&
class Base&
&&&&& friend class Derived2;//friend&
&&&&& protected://protected&
class Derived1:Base//private继承&&
&& /*&&& int getx()
&&&&&&&&&&//不合法,访问基类的private成员&
&&&&&&&&&& }*/&
&&&&&& int gety()&
&&&&&&&&&&//合法,访问基类的protected成员&&
&&&&&&&&&& }&
class Derived2:Base//private继承&&
&&&&& public:&
&&&&&&&&&&&& int getx();&
int Derived2::getx()&
&&&//友员直接访问基类的私有成员&&
class Derived3:public Base//public继承&&
&&&&& public:&
&&&&&&&&& /*&&&
&&&&&&&&& int&& getx()
&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&//在这里还是不能访问,因为x是Base的private成员,只在Base里可以访问,在外面不可以被访问。&
&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&&& */&
&&&&&&&&& int&& gety()&
&&&&&&&&&&&& {&
&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&& }&
int main()&
&&& Derived2//没有带参数的构造函数或成员函数初始化x,构造函数赋个随机值给x&&
&&& i=ob.getx();&
&&& cout&&i&&&
&&& Derived3 ob3;&
&&& i=ob3.gety();&
&&& cout&&i&&&
&&& system(&pause&);&

我要回帖

更多关于 私有构造函数的作用 的文章

 

随机推荐