双冒号::被认为是作用域限定操作符,用来指定类中不同的作用域级别。::左边表示的是作用域,右边表示的是访问的成员。

系统定义了两个作用域,self和parent。self表示当前类的作用域,在类之外的代码是不能使用这个操作符的。

Program List:使用self作用域访问父类中的函数

<?php
class NowaClass
{
function nowaMethod()
{
print '我在类 NowaClass 中声明了。';
}
} class ExtendNowaClass extends NowaClass
{
function extendNowaMethod()
{
print 'extendNowaMethod 这个方法在 ExtendNowaClass 这个类中声明了。';
self::nowaMethod();
}
} ExtendNowaClass::extendNowaMethod(); ?>

程序运行结果:

extendNowaMethod 这个方法在 ExtendNowaClass 这个类中声明了。
我在类 NowaClass 中声明了。

parent这个作用域很简单,就是派生类用来调用基类的成员时候使用。

Program List:使用parent作用域

<?php
class NowaClass
{
function nowaMethod()
{
print '我是基类的函数。';
}
} class ExtendNowaClass extends NowaClass
{
function extendNowaMethod()
{
print '我是派生类的函数。';
parent::nowaMethod();
}
} $obj = new ExtendNowaClass();
$obj -> extendNowaMethod(); ?>

程序运行结果:

我是派生类的函数。
我是基类的函数。

Program List:用基类的方法访问派生类的静态成员

如何继承一个存储位置上的静态属性。

<?php
class Fruit
{
public function get()
{
echo $this->connect();
}
}
class Banana extends Fruit
{
private static $bananaColor;
public function connect()
{
return self::$bananaColor = 'yellow';
}
}
class Orange extends Fruit {
private static $orange_color;
public function connect()
{
return self::$orange_color = 'orange';
}
}
$banana = new Banana();
$orange = new Orange();
$banana->get();
$orange->get();
?>

程序运行结果:

yellow
orange。

Program List:静态函数初始化

在一个类中初始化静态变量比较复杂,你可以通过创建一个静态函数创建一个静态的构造器,然后在类声明后马上调用它来实现初始化。

<?php
class Fruit
{
private static $color = "White";
private static $weigth;
public static function init()
{
echo self::$weigth = self::$color . " Kilogram!";
}
}
Fruit::init();
?>

程序运行结果:

White Kilogram!

Program List:一个简单的单例模式例子

这个应该可以帮到某些人吧。

<?php
class Fruit
{
private static $instance = null;
private function __construct()
{
$this-> color = 'Green';
}
public static function getInstance()
{
if(self::$instance == null)
{
print "Fruit object created!<br />";
self::$instance = new self;
}
return self::$instance;
}
public function showColor()
{
print "My color is {$this-> color}!<br>";
}
public function setColor($color)
{
$this-> color = $color;
}
}
$apple = Fruit::getInstance(); // Fruit object created!
$apple-> showColor(); // My color is Green!
$apple-> setColor("Red");
$apple-> showColor(); // My color is Red!
$banana = Fruit::getInstance();
$banana-> showColor(); // My color is Red!
$banana-> setColor("Yellow"); $apple-> showColor(); // My color is Yellow!
?>

程序运行结果:

Fruit object created!
My color is Green!
My color is Red!
My color is Red!
My color is Yellow!

转载请注明:老高的自留地 » PHP作用域限定符::的几个程序例子

在访问PHP类中的成员变量或方法时,如果被引用的变量或者方法被声明成const(定义常量)或者static(声明静态),那么就必须使用操作符::,反之如果被引用的变量或者方法没有被声明成const或者static,那么就必须使用操作符->。

另外,如果从类的内部访问const或者static变量或者方法,那么就必须使用自引用的self,反之如果从类的内部访问不为const或者static变量或者方法,那么就必须使用自引用的$this。

静态变量
是只存在于函数作用域的变量, 不过, 在函数执行完成后,这种变量的值不会丢失,也就是说, 在下一次调用这个函数时,变量仍然会记得原来的值. 要将某个变量定义为静态的, 只需要在变量前加上 static 关键字即可.

类中静态元素的使用
在类中, static 关键字有两种主要用法, 一是用来定义静态成员,一是用来定义静态方法. 在类的内部, 可以使用作用域限定符 (::) 来访问不同层次作用域的变量.


静态成员
静态成员是一种类变量, 可以把它看成时属于整个类而不是属于类的某个实例. 与一般的实例变量不同的是, 静态成员只保留一个变量值, 而这个变量值对所有的实例都是有效的, 也就是说, 所有的实例共享这个成员.
$this 只表示类的当前实例, 而 self:: 表示的是类本身,在类之外的代码中不能使用这个操作符,而且它不能识别自己在继承树层次结构中的位置.
也就是说, 在扩展类中使用self 作用域时, self 可以调用基类中声明的方法, 但它调用的总是已经在扩展类中重写的方法. 与$this 不同的是, 在使用静态变量时,必须在作用域限定符后面加上$符号.
在扩展类中, 在基类的方法被重写的情况下,使用 parent 作用域调用定义在基类中的方法.静态成员也可以只属于父类. 如果在子类和父类中同时声明了某个成员,也可以使用parant:: 在子类中访问父类中的变量. 在这种情况下, 父类的静态成员和子类的静态成员保存的是不同的值.
可以在 :: 操作符的左边写上类的名称来静态地访问某个成员, 这样避免创建类的实例. 不仅省略掉实例化类的代码, 而且还会更高效, 因为类的每个实例都会占用一小部分的系统资源.
在使用 :: 操作符访问成员变量时, 需要再次注意对$符号的使用. 因为PHP当前不支持动态的静态变量的使用, 也就是说不支持可变的静态变量. 在使用$this->$var时, 被访问的成员是包含在$var中的变量的值. 而不用$符号访问某个变量实际上查找的是类的某个常量而常量是不能通过$this来访问的.
PHP6 中提出的static:: 作用域使我们不再需要使用self:: 和parent::. 当希望指向最终的实现功能的类时, 就可以使用static::, 这个限定符会在代码执行之前立即计算出继承层次机构上最后那个类的成员. 之一过程被称为延迟绑定, 它使我们可以在子类中重写某个静态变量, 并且也可以从某个在父类中声明的函数中反问这个最终成员.


静态方法
静态方法和非静态方法之间有一个重要的区别: 在调用静态方法时, 不再需要拥有类的实例.
静态方法和非静态方法使用原则:
一是如果某个方法中不包含$this 变量, 就应该时静态方法; 如果不需要类的实例, 可能还应该使用静态类, 这样可以免去实例化类的工作. 另, 在静态方法中时不能使用$this 变量的, 因为静态方法不属于某个特定的实例.

PHP中使用作用域限定操作符时, 用变量作为类的名称时不允许的.

  • parent:: 可用于调用父类中定义的成员方法。
  • parent::的追溯不仅于直接父类。
注:
在类里面的时候,$this->func()和self::func()没什么区别。
在外部的时候,->必须是实例化后的对象使用; 而::可以是未实例化的类名直接调用。
举个例子:
class Mytest{
function ccc($str){
echo $str;
}
}
Mytest::ccc("123456");
$object = new Mytest();
$object->ccc("123456"

php是个很诡异的语言。当然,这是对学习过C++或者Java等面向对象语言的人来说。
php可以定义静态的方法,然后通过className::staticMethod()形式来调用。非静态的方法,当然通过classObject->nonStaticMethod()使用。这个其他语言中也是如此,没什么大惊小怪的。可是,您能用className::nonStaticMethod()调用非静态方法吗?这方面恐怕Java和C++要冷汗直流了。其实,php自己也是满脸黑线。为什么这么说呢?
先来看看面向对象的静态和非静态。面向对象的语言中,都会支持静态方法。静态方法,属于类的固定资产的;非静态的方法,属于类的实例的私有财产。在内存中,静态方法,对于整个类也就只存了这么一份;无论你new了多少个实例对象,它们共用的也就这么一份。对于非静态的就不一样了,你new几个,内存就给你new几份。另外,静态方法内不可以调用非静态方法,非静态方法内却可以调用静态方法。这个就是静态和非静态的区别。
面向对象用static关键字来定义静态。未被标明是静态的方法,是不可以用类名加两个冒号的形式调用的。php和其它很有区别的一点就是这个了:php中未被标明是静态的方法,也可以用类名加两个冒号的形式调用。那么为什么php有这种语法?又为什么感到很无奈呢?
-----以下说明php无奈的故事据相关资料改编,已经过演义处理--------
php之所以发展如此迅速,得益于它的速度。作为脚本语言,php追求高速度和简洁方便,所以选择了解释执行和使用过程方法。后来为了与国际接轨,引入了面向对象的概念。而就是在引入这个面向对象的特征时,发生了一件令php目瞪口呆,最终无可奈何的事情。面向对象有个很重要的概念就是继承。在继承中,子类如果覆盖父类的方法,同时需要调用父类的同名方法,该怎么办呢?php4版本提供了这样一种方法:parentClassName::method()。提出此种方法之时,有识之士已经发现了问题:这个调用方式,不正是静态方法的调用方式吗?php4随即答曰:不碍事。类中使用时,可以判断此方式为子类正在调用父类方法;非类中使用时,就判断为静态调用了。所需要的只是发现此种调用时查询一下方法映射就好了。其实,一想,也确实是那么回事。php4后来想想,如果每次调用都检验一下此次调用是否合法,那多少也会影响性能,不如这个问题就交给程序员去控制吧:他们会知道只在类中使用此形式的调用的。唉,可惜天不遂人愿。php4低估了程序员们的创造力!程序员们很快发现了这个方式,并且不余遗力地使用起来。许多集成的API也开始使用这种怪癖的方式。php无奈了,随即在php5中引入了另一种方式,使用关键字parent来调 用父类函数:parent::method()。但是,想要放弃php的非静态方法的静态调用,着实是不再可能了。
--------------------------------------------------------
不过,话说回来,这种php的怪癖方式,有什么好处吗?性能和内存占用方面如何呢?
于是我开始推理了:定义了一个非静态的方法,静态调用时,php首先转换此方法为静态定义,加载入静态内存区域,然后执行。通常一次业务,只使用一个业务处理类中的一个方法,如果使用非静态定义,静态调用,内存中岂不是只加载了这个业务类中的一个方法,不是就实现了静态方法的按需加载吗?岂不是要省下一部分内存?性能方面,无论是静态调用,还是对象调用,反正都是执行一个方法,性能还不是一样?并且静态调用非静态定义方法还省了一个new语句。嗯,嗯。这么想的同时,手就开始写上了。
那么实际如何呢?我做了一个小测试。


PHP code
t::start();t::end(); //消除t类首次加载的影响
t::start();
model_profile_base::getBaseInfo($uid);
t::end();


t::start();
$model = new model_profile_base();
$model->getBaseInfo($uid);
t::end();


t::start();
$model = new model_profile_base();
$model->getBaseInfo($uid);
t::end();

model_profile_base是处理基本资料的一个业务类,比较复杂,比较接近于项目中的业务处理实际。
下面是用到的计时和统计内存的t类的定义:

PHP code
<?php
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
class t{
static $start_time;
static $end_time;
static $start_memory;
static $end_memory;

public static function start()
{
self::$start_memory = memory_get_usage();
self::$start_time = microtime_float();
echo '<br/>Start @'.self::$start_time.'('.self::$start_memory.')|------->';
}

public static function end()
{
self::$end_time = microtime_float();
self::$end_memory = memory_get_usage();
echo 'End @'.self::$end_time.'('.self::$end_memory.') :';
echo '|======= 共耗时:'.(self::$end_time-self::$start_time).',共用内存:'.(self::$end_memory-self::$start_memory);
}
}

第二行是静态调用非静态方法,第三行是正常调用非静态方法。然后,我发现我的推理悲剧了。刷了好几次页面,统计结果在数量级上都差不多。静态调用非静态方法无论内存占用还是性能上都不敢恭维。这样的结果有点令人咂舌。
那么,再试一下循环执行多次的结果:

PHP code
t::start();t::end(); //消除t类首次加载的影响
t::start();
for($i=0; $i<1000;++$i) model_profile_base::getBaseInfo($uid);
t::end();

t::start();
$model = new model_profile_base();
for($i=0; $i<1000;++$i) $model->getBaseInfo($uid);
t::end();

于是更让人无语的结果出来了:

PHP code
Start @1287562243.5799(1009372)|------->End @1287562243.5799(1009372) :|======= 共耗时:3.0040740966797E-5,共用内存:0
Start @1287562243.58(1009372)|------->End @1287562244.1532(1587544) :|======= 共耗时:0.57321000099182,共用内存:578172
Start @1287562244.1532(1587544)|------->End @1287562244.6921(1587744) :|======= 共耗时:0.53887605667114,共用内存:200

除了两种方式时间上开始接近外(并且还是正常调用比较利索),内存上仍然有天壤之别。失望之余,查了下网上,发现也有人做了类似的测试。我就直接把结果拷上来吧:
(可能光看结果,会感觉有点难于理解,可以在这里找到详细说明:http://vega.rd.no/articles/php-static-method-performance)

测试结果 (ORDER BY time DESC):

PHP code
============Which method========================Time======
Inline calculation 0.0805 s
Normal function call 0.3438 s
Normal method called through object 0.4118 s
Static method called statically 0.4280 s
Unspecified method called through object() 0.4294 s
Unspecified method called statically() 0.6960 s

如此看来,静态调用非静态方法在性能和内存上都不占优势;另外,此种调用方法容易产生维护混乱。那么,来个短而有力的总结:静态调用非静态方法不可取。
http://www.dabaoku.com/jiaocheng/biancheng/php/201102248955.shtml


有一个原则 如果某个方法中不包含$this变量 那么这个方法就应该是静态方法。如果不需要类的实例,可能还需要使用静态类。在静态方法中不能使用$this变量。

php作用域限定符的更多相关文章

  1. C++ 类学习笔记 :: 作用域限定符

    类与封装的概念: 1.类: 类的实现.类的使用. 当使用类的时候,不需要关心实现的细节.当创建类的时候,才需要考虑到内部具体的实现细节. 2.类的封装: 成员变量,C++用于表示属性的变量. 成员函数 ...

  2. C++作用域限定符:private/public与protected

    C++的作用域限定符其实涉及到了C++特性中的封装和继承. public/private:涉及类的封装特性.对于一个类需要对外展示的部分,我们可以将其声明为public,对于不希望对外展示的,我们将其 ...

  3. (:: operator)作用域限定符的几种使用

    一.(:: operator)解决局部变量与全局变量重名问题 ; int main() { ; cout<<var<<endl;//local variable cout< ...

  4. C++const限定符

    在C语言中我们使用#define宏定义的方式来处理符号常量.而在C++中有一种更好的处理符号常量的方法,那就是使用const关键字来修改变量声明和初始化.这种处理常量方式的好处不言而喻:如果程序在多处 ...

  5. const 限定符

    1.定义const对象 const限定符把一个对象转换成一个常量 const int Bufsize = 512; 定义Bufsize 为常量并初始化为512.变量Bufsize仍然是一个左值,但是不 ...

  6. C++ Primer 第二章 引用 指针 const限定符

    1.引用: 为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d也就是声明的变量名(声明符就是变量名). PS:1.通过图片中编译所提示的报错信息 ...

  7. C++ const 限定符

    C++ const 限定符 作用:把一个对象转换成一个常量 用法:const type name = value; 性质:1. 定义时必须初始化,定义后不能被修改.2. 类中的const成员变量必须通 ...

  8. const限定符的作用

    const限定符的作用:                                     1.定义const常量:const可以将一个对象变成一个常量,不可被修改,所以定义的 时候必须进行初始 ...

  9. 变量和基本类型——复合类型,const限定符,处理类型

    一.复合类型 复合类型是指基于其他类型定义的类型.C++语言有几种复合类型,包括引用和指针. 1.引用 引用并非对象,它只是为一个已存在的对象所起的另外一个名字. 除了以下2种情况,其他所有引用的类型 ...

随机推荐

  1. JavaIO流(输入输出操作)

    Java中执行输出和输入操作,需要通过IO流.例如最常见的System.out.println()就是一个输出流.IO流的类比较多,但核心体系就是由File. InputStream .OutputS ...

  2. 【css】 如何修改select的样式

    select { /*清除select默认样式*/ appearance:none; -moz-appearance:none; -webkit-appearance:none; -ms-appear ...

  3. linux 学习第十一天

    一.配置服务说明 1.1.linux系统中的一切都是文件 1.2.配置一个服务就是在修改去配置文件 1.3.要想让新的配置文件立即生效,需要重启对应的服务 二.配置网卡 2.1.编辑配置文件 vim ...

  4. (1)linux和oracle---环境搭建

    对linux和oracle一直是敬而远之,稍微有些了解.无奈由于工作需要这次要硬着头皮上了!@#!@@#$%^^ 对于重windows用户的我来说,简直是万种折磨. 算是做个记录吧,一定要坚持下去. ...

  5. python写员工信息表作业笔记

    需求 流程图

  6. PHP程序员学Objective-C之后的变化

    趣味坎谈,不一定100%准确,以自己的实际情况为准; 如题,我2008年开始学PHP,PHP是我学的第二门编程语言,一直用到现在,2010年初开始做iOS开发,学习了Objective-C,学这2门语 ...

  7. [笔记] FireDAC DataSet 导入及导出 JSON

    刚好需要将 FireDAC DataSet (TFDDataSet, TFDQuery...) 转成 JSON,网上找了一圈,原来从 XE6 开始就支持这个功能了: 储存: DataSet1.Save ...

  8. STM32F4 SPI双机通讯调试总结

    1.如果查询方式进行数据收发,不管是Master,还是Slave,流程如下:(假设收发n+1个字节) a.等待TXE,写入一个字节 b.等待TXE,写入一个字节 c.等待RXNE,读取一个字节 循环b ...

  9. jdbc之Statement和Preparement

    Jdbc DML 操作 Statement:静态SQL操作 每次操作都会将sql语句提交到数据库执行一次,性能比较低 // 1.加载驱动程序 Class.forName(driverName); // ...

  10. python教程(三)·自定义函数

    前面介绍了如何使用函数,这一节我们就来学习怎么创建自己的函数! 自定义函数 创建函数非常简单,它使用关键字 "def",下面的代码创建了一个带有一个参数的函数,并使用不同的参数调用 ...