C++ 11 新特性

目录

  • 1. 支持特性的编译器版本
  • 2. 模板表达式中空格
  • 3. 空指针
  • 4. auto
  • 5. 统一初始化
  • 6. explict
  • 7. 范围for
  • 8. =default,=delete
  • 9. 化名模板(alias template)
  • 10. using
  • 11. noexcept
  • 12. override
  • 13. final
  • 14. decltype
  • 15. lambda
  • 16. Variadic Templates(可变参数模板)
    • 16.1 可变参数函数模板
      • 16.1.1 不同类型参数的分解
      • 16.1.2 同类型参数的分解
    • 16.2 可变参数类模板
      • 16.2.1 递归继承参数分解
      • 16.2.2 递归内含参数分解
      • 16.2.3 头尾元素处理方式不同
  • 17. 右值引用
  • 18. unorderd容器
  • 19. tuple类型

1. 支持特性的编译器版本

(1)不同编译器版本支持的C++新特性可在 https://zh.cppreference.com中查看:
在这里插入图片描述
在这里插入图片描述
(2)可以在cpp程序中打印宏定义“__cplusplus”,看当前编译器支持C++ 98、C++ 11、C++ 14等。
在这里插入图片描述

2. 模板表达式中空格

          C++ 11之前,模板表达式中两个方向相同的尖括号必须用空格隔开,否则会被编译器解析为流操作符>>。C++ 11后,无需用空格隔开。
在这里插入图片描述

3. 空指针

          C++ 11之前,指针初始化或者处理空指针都是将其赋值为0或NULL。该方法有一种缺陷,即这种赋值下的指针可以被当作一个整数值使用,缺少了指针类型这一性质。C++ 11后,初始化或者处理空指针应该用nullptr,其附有指针类型的性质。
在这里插入图片描述

4. auto

          auto关键字可以让编译器自动进行类型推导,主要用于代替长和复杂表达式的类型书写
在这里插入图片描述

5. 统一初始化

          C++中初始化变量和对象的方式有( ),{ },=。其中,{ }可以进行任何变量和对象的初始化:变量类型 变量名 {初始化参数 }。底层采用的是initialzer_list接收初始化参数。C++中编译器将{ 参数…}打包为initialzer_list对象
在这里插入图片描述

  • { }可以用来设初值
    在这里插入图片描述

  • { }不允许窄化参数,否则编译器可能会warning或error:
    在这里插入图片描述

std::initializer_list是标准库中的一个模板,所有的标准容器的构造函数都有以initializer_list为参数的构造函数。
在这里插入图片描述

  • 通过将initializer_list作为函数的形参,可以给函数传输任意数量的参数,在函数内部可用initializer_list的迭代器对每个参数进行遍历。

  • initializer_list底层为一个array,里面存储每个参数的指针,可以将其看作一个辅助容器。

  • 由于initializer_list构造函数是private,所以其只能由编译器调用并构造出initializer_list对象。

  • 当具有特定个数参数的重载函数与有initializer_list参数的重载函数共同存在,并且输入该特定个数的参数时,有initializer_list参数的重载函数优先被调用

  • 如果没有接收initializer_list参数版本的function函数,也可以用{ }传递参数,只不过编译器会将{ }中的参数拆解,寻找符合调用的function
    在这里插入图片描述

initializer_list应用的例子:

在这里插入图片描述

6. explict

          explict关键字一般用于修饰构造函数,阻止编译器隐式利用class A的构造函数将其他类型的数据转换为class A类型的对象。C++ 11之前,explict仅阻止编译器将单一参数(只有一个待定的参数)的构造函数作为类型隐式转换的工具。C++ 11之后,explict可以阻止有多个参数的构造函数作为类型隐式转换的工具
在这里插入图片描述
在这里插入图片描述
          我在侯捷老师在多参数验证explict的结果上有一些不一样的观点。两个红色框框在支持C++ 11版本的g++编译器是并未报错。除非将接收initializer_list参数的构造函数注释掉,才出现如上图的结果。因为复制初始化列表 P p3 = {77, 5, 44},应该首先用{77, 5, 44}生成initializer_list对象,之后需要initializer_list对象隐式转换为P对象,无论如何也不会去调用显式的explicit P(int a, int b, int c)构造函数,只会隐式调用P(initializer_list<\int>)。

#include <iostream>

using namespace std;
class P
{
public:
    P(int a, int b)
    {
        cout<< "P(int a, int b) \n";
    }

    P(initializer_list<int>)
    {
        cout<<"P(initializer_list<int>) \n";
    }

    explicit P(int a, int b, int c)
    {
        cout<<"explicit P(int a, int b, int c) \n";
    }
};
void fp(const P&) {};

int main()
{
    P p1 = {77, 42};
    P p3 = {77, 5, 44};
    P p2 (77, 5,2);
    fp( P{47, 11} );
    fp( P{77,5,42});
    cout<<__cplusplus<<endl;
    return 0;
}

在这里插入图片描述

7. 范围for

          范围for可用于遍历容器里面的元素,简化书写。依次从coll容器中取出元素赋值给decl变量(可能存在隐式转换,如果不想发生请将引起隐式转换的构造函数声明为explicit)。不过赋值过程是拷贝。如果需要实际操作容器中的元素,则需要将decl变量声明为引用。set、map等容器不允许通过迭代器改变元素值,因此无法使用范围for。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

8. =default,=delete

          C++编译器有时候会为类声明一些默认成员函数(默认构造、拷贝构造、move拷贝构造、拷贝赋值、move拷贝赋值、析构函数、操作符函数)。如果实现了这些函数的自定义版本后,编译器就不会去生成默认版本。大多数时候,我们需要声明带参数的构造函数,此时编译器就不会为其生成默认构造函数(无参)。如果此时还是希望编译器能帮我们自动生成一个默认构造(无参),则可以使用=default告诉编译器。=default可以用于默认构造、拷贝构造、move拷贝构造、拷贝赋值、move拷贝赋值、析构函数如果类中有指针成员,那么需要自行撰写上述特殊成员函数;若无指针成员,则可以直接使用默认

class Example
{
public:
    Example() = default;  // 编译器将为其生成一个默认构造函数
    Example(int i) : data(i) {}

private:
    int data;
};

有时候可能不希望让编译器悄悄地帮我们生成一个默认函数,C++ 11可以直接令函数=delete,告诉编译器不要帮我生成该默认函数,=delete也可以禁止某函数被使用,可用于任何成员函数

  • 禁止类型隐式转换,也可以用在普通函数上。
class Example
{
public:
    Example(int i) {}
    Example(char c) = delete;
};
int main()
{
    Example t1(1);
//   Example t2('a');  // 无法通过编译,由于重载特性,优先调用最佳匹配函数Example(char c),但该函数为delete
}
  • 禁止对象被拷贝:以前可以将拷贝构造、拷贝赋值声明为private来实现禁止对象被拷贝,现在可以使用delete实现。
    在这里插入图片描述
    在这里插入图片描述

9. 化名模板(alias template)

          化名模板是给模板起一个别名,本质上还是模板
在这里插入图片描述

  • 可以简化模板书写

  • 个模板被当作另一个模板的模板参数时,可以用模板的化名,以化名将模板本身传入另一个模板的模板参数中。直接传入模板的名字或者临时对象,编译器均不认识,因为一般编译器只对实例化后的模板对象进行拷贝传递;
    在这里插入图片描述
              采用模板的化名传递模板,作为模板的模板参数
    在这里插入图片描述

  • 不允许对化名模板做特化
              化名模板类似于模板的typedef,但比模板的typedef更强大,typedef不接受变化的参数,而化名模板可以。
    在这里插入图片描述

10. using

          using关键字可以用于展开命名空间,声明类成员。C++ 11后,using还可以被用于类型化名(类似typedef),化名模板
在这里插入图片描述
在这里插入图片描述

11. noexcept

          noexcept关键字用于表示一个函数或表达式在执行期间是否会抛出异常,相当于函数或表达式的作者对编译器和使用者承诺绝不抛出异常,从而使得编译器可以对其进行优化、方便使用者调式代码。noexcept后面可以紧跟一个条件判断结果,表示在条件判断为真的情况下,该函数绝不抛出异常。
在这里插入图片描述
          下例表示如果x.swap(y)函数不抛出异常,外层的swap()函数就绝不会抛出异常。
在这里插入图片描述
          当使用自动增长型的容器(例如vector、deque等)时,标准库容器为了提高效率会调用move copy,前提是容器存放的元素对象的move copy构造和move copy赋值被声明为noexcept,否则标准库容器将不会使用move copy。
在这里插入图片描述

12. override

          override关键字表示如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译。避免程序员将需要重写的函数写错为重载。
在这里插入图片描述

13. final

          当final关键字用于修饰类时,声明该类已经是整个继承体系的最后一种派生类了,其他类将无法继承该类
在这里插入图片描述

          当final关键字用于修饰虚函数时,声明该类已经是整个继承体系中最后一个能重写该虚函数的派生类了,后续继承该类的派生类中不允许在重写该虚函数了。
在这里插入图片描述

14. decltype

          decltype可以获取对象或表达式的类型。类似的关键字有typeof,但是它不是标准库中的。

  • 获取对象类型:
    在这里插入图片描述

  • 获取表达式(包括lambda表达式)类型:(下例表明返回值类型为x+y的类型)
    在这里插入图片描述
    在这里插入图片描述

15. lambda

          lambda表达式被用来创建一个匿名函数,加上()后表示调用,可替换独立函数或者函数对象,基本上可以等效于仿函数对象。lambda表达式的格式如下:
在这里插入图片描述

  • […]为取用外部的数据变量,里面的数据是在lambda表达式创建的时候就于其绑定好了,默认是拷贝赋值[=],[&]表示将外部的数据变量以引用的形式绑定到lambda表达式上
  • (…)为lambda表达式被调用时传入的新参数,无传入参数则可以不写(),前提是mutable、throwSpec、retType都不写
  • mutable关键字表示[…]中的数据是可以被改写的,一般[=]拷贝得到的数据均是可以改写的,因此也要配套使用mutable关键字,[&]的参数无需用mutable
  • throwSpec表示是否抛出异常;
  • ->表示后面将要写返回值类型
  • retType为lambda返回值类型
    例子:
    在这里插入图片描述
    在这里插入图片描述
               由于lambda本身是一种C++ 语法,并不是真正的类对象,没有构造函数等等。所以不建议用lambda来构造其他对象,因为如果构造其他对象时没有传入lambda实例,其他对象的构造函数可能会去调用lambda构造函数,从而导致报错。
    在这里插入图片描述
              标准库中,lambda表达式最常用在一些algorithm中,当作仿函数传入算法中进行执行,效率比真实的仿函数高一点点。
    在这里插入图片描述

16. Variadic Templates(可变参数模板)

16.1 可变参数函数模板

          在 Variadic Templates中,模板参数个数可以是不固定的,下例中,用typename T来表示模板的第一个参数,用typename… Types表示模板剩余的所有模板参数(简称模板参数包)。注意模板参数包声明时…与传入模板函数时的…位置不一样。可变参数模板根据所传入的模板参数进行实例化。

template <typename T, typename... Types>
void printx( const T& firstArg, const Types&... args){
	cout << firstArg << endl;l l print first argumentprintX(args...);
}

16.1.1 不同类型参数的分解

          可变参数模板有一个重要的应用,就是结合递归调用模板自身,将所有的模板参数一一分解开来,分别进行处理。下例中,print函数模板参数由一个固定模板参数T和一个不固定模板参数包Types组成,print实例中有四个模板参数,那么print第一个模板实例就是接收firstArg接收第一个参数7.5,剩下的三个参数全部由args接收。print内部又将包含剩余参数的参数包作为参数递归调用print。递归调用的第一个print将收到第一个参数为“hello”,和剩下两个参数组成的参数包,总是分解成1+若干参数…,直到递归分解直至调用终止的无参print( )函数
在这里插入图片描述

  • sizeof…(args)输出args参数个数;
  • 在同一个函数的不同模板中,template<typename T, typename… Types>比template<typename… Types>更特化,因此上例中只会调用1和2。

16.1.2 同类型参数的分解

          若需要分解的参数是同类型的,那么可以直接用initializer_list替换可变参数模板
在这里插入图片描述
在这里插入图片描述

16.2 可变参数类模板

16.2.1 递归继承参数分解

          可变参数模板也可以用在类模板中,一个常用的技巧就是通过递归继承分解模板参数。C++新特性中tuple就是利用了这中技巧。tuple容器可以将不同数据类型的元素放在一个容器中。其基本原理是将模板参数分解为1+args(若干参数),创建第一个类型参数的成员,并继承只包含args参数的模板类,从而依次递归实现不同类型参数的分离与成员变量的创建。当需要对tuple对象取元素时,直接将子类的指针转换为指向父类的指针即可。如下图所示(typename Head::type 可直接改为Head(本就是参数类型,内置类型无::type,例如无int::type)):
cc

16.2.2 递归内含参数分解

          与递归继承不同,可以采用递归内含剩余模板参数类型的类,实现分解不同模板参数并构造对应的对象
在这里插入图片描述

16.2.3 头尾元素处理方式不同

          当处理头尾参数与处理中间参数的方式不同时,则可以借用可变参数模板分解参数的同时,根据参数的索引序号进行特殊处理,其中是否是尾参数可用sizeof…()获取可变参数个数
在这里插入图片描述

17. 右值引用

         首先需要理解左值与右值:

  • 左值为存储在内存中、有明确存储地址(可取地址)的数据;另一种分辨方法就是能够出现在‘=’的左边的为左值
  • 右值为可以提供数据值的数据(不可取地址);另一种分辨方法是只能出现在‘=’的右边的为右值(规定临时对象是一种右值)
    在这里插入图片描述

         右值引用是C++ 11的新特性,它是对一个右值进行引用的类型(&&),主要为了避免不必要的拷贝造成资源的浪费和参数转发。由于右值是匿名的,因此只能通过引用它的方式找到它。通过右值引用的声明,该右值又”重获新生”(相当于给匿名对象重新起了名字且不会马上析构),其生命周期与右值引用类型变量的声明周期一样,因此“右值引用的结果”是左值当右值出现在赋值符号‘=’的右侧时,我们认为可以让编译器将右值的资源直接搬移(偷取)给左值而不需要重新拷贝再析构右值。右值引用一般用于撰写move版本的copy构造函数和move版本的copy 赋值函数,内部实现只是将原来指向资源的指针赋值为null,新对象的指针接管原来的资源,本质上是一个浅拷贝过程,因此效率高。输入是右值且要执行拷贝动作时,优先调用move版本的拷贝构造函数。
在这里插入图片描述
         标准库中std::move()函数可以将左值转换为右值,但是使用者必须确保后续不再使用被转换的左值(左值的资源已经被转移走了)。下例中,调用普通拷贝构造函数构造c1,调用move拷贝构造函数(右值引用)构造c2且后续不允许再使用
在这里插入图片描述

         当存在多层函数嵌套调用时,参数的右值特性可能会被丢失。标准库中std::forward()函数用于保持参数类型信息的完美转发
在这里插入图片描述
在这里插入图片描述
         调用std::forward时,根据参数的左值或右值属性,编译器会选择适当的模板实例进行转发。下图为std::forward源码。如果输入参数是一个左值引用(int &),std::forward将返回一个左值引用(int & && 折叠为 int &)。如果参数是一个右值引用(int &&),std::forward将返回一个右值引用(int && && 折叠为int &&)
在这里插入图片描述
         根据模板类型推导,如果输入参数是一个左值引用(int &),__t和std::remove_reference<_Tp>::type &的类型为int &,std::remove_reference<_Tp>::typede 类型为int , 则_Tp为int & ,因此返回值转型为_Tp&&,即为int & &&,根据折叠原理折叠为int &。同理输入右值引用的到int &&。

完整的带move拷贝的类例子:
在这里插入图片描述
在这里插入图片描述

18. unorderd容器

         C++ 11后,将原来的无序容器名字进行了更换
在这里插入图片描述

         用到底层由哈希表实现的容器时,需要传入一个计算hash值的函数对象。由于有的对象可能包含许多不同类型的成员变量,如何计算hash值存在困难。一种万用的hash function如下:
在这里插入图片描述

19. tuple类型

         tuple是C++11新标准的类型。tuple可以存放任意个数任意类型的元素, 可以使用直接初始化, 和"make_tuple()"初始化, 访问元素使用"get<>()"方法, 注意get里面的位置信息, 必须是常量表达式(const expression);
在这里插入图片描述
(本文为学习侯捷老师C++2.0新特性课程后的笔记)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141209.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3个ui自动化测试痛点

当我们找工作的时候查看招聘信息发现都需要有自动化测试经验&#xff0c;由此看来测试人员不会一点自动化测试技术都不好意思说自己是做软件测试的。大部分测试人员也都是从使用自动化测试工具、录制回放、测试脚本、开发小工具入门自动化测试的&#xff0c;然后在慢慢的接触 U…

Ubuntu中安装R语言环境并在jupyter kernel里面增加R kernel

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

时间序列预测(2) — 时间序列预测数据集

目录 数据集1&#xff1a;GEFCom2014负荷数据 数据集2&#xff1a;爱奇艺用户留存预测挑战赛数据集 数据集1&#xff1a;GEFCom2014负荷数据 数据集下载&#xff1a; 百度网盘&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1PgCWHx8vYUfGB9UGtCmaVA?pwdktn0 提取码…

基于SSM+Vue的网上购物商城

基于SSMVue的网上购物商城的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringMyBatisSpringMVC工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 商品详情 登录界面 管理员界面 用户界面 可视化图标 摘要 基于SSM&#xff0…

JavaScript_动态表格,实现全选,全不选,反选功能,点击第一个复选框,获取所有的checkbox,给所有tr绑定鼠标移到元素之上和移出元素事件。

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>表格全选、全不选&#xff0c;反选</title><style>table{border: 1px solid;width: 500px;margin-left: 30%;}td,th{text-align: center;b…

Web APIs——BOM和延迟函数setTimeout

1、window对象 1.1 BOM&#xff08;浏览器对象模型&#xff09; BOM&#xff08;Browser Object Model&#xff09;是浏览器对象模型 window对象是一个全局对象&#xff0c;也可以说是JavaScript中的顶级对象像document、alert()、console.log()这些都是window的属性&#xf…

POWER APPS:必填项功能

Power apps的Forms组件中&#xff0c;它的卡片有个属性为Required。作用为提醒此项为必填&#xff0c;且在submitform时检查此项是否有值&#xff0c;没有值就无法正常提交&#xff0c;且会有提示。 另外可对提交按钮的属性displaymode做一次判断&#xff0c;只允许按钮在窗体中…

JDK更换版本不生效问题

JDK版本更换 问题: 当本地电脑拥有多个版本jdk时, 切换jdk版本不生效 解决方案: 1.查看环境变量(高版本的jdk安装时自动注入环境变量) 2.将Path里面的jdk的bin配置上移到最上面 3.查看jdk版本, java -version 启动项目,显示如下使用了jdk17

windows 使用命令关闭进程

1.使用cmd命令打开命令窗口 2.输入命令&#xff1a;netstat -aon|findstr 端口号 查询出该端口进程、以及进程pid 3.使用查询出来的pid杀掉进程&#xff0c;输入命令&#xff1a;taskkill /pid 32188 /f

STM32F4 GPIO端口二极管作用——二极管钳位作用

如上图所示&#xff0c;有两个保护二极管&#xff0c;用于保护内部电路&#xff0c;防止I\O引脚外部过高或者过低的电压输入时造成内部电路损坏。 具体来讲&#xff1a;当引脚输入电压高于VDD时&#xff0c;上面的二极管导通&#xff0c;输入点电压被钳位到约VDD0.7V&#xff…

财报解读:加码布局中国市场,阿迪达斯能否再次华丽转身?

“哪里有世界冠军&#xff0c;哪里就有阿迪达斯”是过去人们对阿迪达斯江湖地位的肯定。作为体育运动用品行业的元老&#xff0c;阿迪达斯全球运动市场份额排名第二&#xff0c;是第一个进入中国市场的国外鞋服运动品牌&#xff0c;曾连续23个季度在大中华区实现销售额两位数增…

activiti7审批驳回,控制变量无法覆盖,导致无限循环驳回,流程无法结束

项目开发过程中使用工作流&#xff0c;因此考虑使用activiti7做完工作流引擎。项目开发过程中&#xff0c;发现流程驳回时&#xff0c;再次执行流程&#xff0c;控制变量无法覆盖&#xff0c;导致无限循环驳回&#xff0c;流程无法结束。流程图如下图所示&#xff1a; 驳回控制…

JVM:如果是你,你如何解决跨代引用的问题?(记忆集和卡集)

这部分内容主要是为了稍后介绍各款垃圾收集器时做前置知识铺垫&#xff0c;如果对这部分内容感到枯燥或者疑惑&#xff0c;可以先放下看&#xff0c;等后续遇到要使用它们的实际场景、实际问题时再结合问题&#xff0c;再回来阅读和理解。 记忆集和卡集 前面在分代收集理论那…

OpenGL_Learn10(颜色)

1. 颜色 我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色&#xff0c;而是它所反射的(Reflected)颜色。换句话说&#xff0c;那些不能被物体所吸收(Absorb)的颜色&#xff08;被拒绝的颜色&#xff09;就是我们能够感知到的物体的颜色。例如&#xff0c;太阳光…

京东商品详情API接口使用方法以及示例代码,可高并发请求

京东商品详情API接口是一种用于获取京东商品详细信息的接口。通过该接口&#xff0c;开发人员可以获取到商品的ID、名称、价格、销量、评价等信息&#xff0c;从而进行进一步的数据分析和应用开发。本文将介绍京东商品详情API接口的使用方法、注意事项以及示例代码。 一、使用…

Python语言的十大特性。

文章目录 前言一、Python二、Python 编程语言的特性三、开源四、Python 中的 GUI 编程支持五、Python 支持高级语言六、可扩展性七、可移植性八、大型标准库九、解释性语言十、面向对象程序设计语言十一、表达力十二、常见问题总结Python技术资源分享1、Python所有方向的学习路…

第四章mlp

生成数据集 读取数据集 data.TensorDataset(*data_arrays)mlp训练 loss nn.CrossEntropyLoss(reductionnone)我要掌握所有人脖颈上的绳 权重衰减 简单概述就是在标准意义的loss函数&#xff08;label值和计算值的差别&#xff09;中再加上一个 惩罚项&#xff0c;为什么要…

A2Attention模型介绍

A2Attention的核心思想是首先将整个空间的关键特征收集到一个紧凑的集合中&#xff0c;然后自适应地将其分布到每个位置&#xff0c;这样后续的卷积层即使没有很大的接收域也可以感知整个空间的特征。第一级的注意力集中操作有选择地从整个空间中收集关键特征&#xff0c;而第二…

Rocky DEM 高尔顿板 小球掉落正态分布模拟

Rocky DEM 高尔顿板 小球掉落正态分布模拟 前言一、外部三维模型的建立二、导入到Rocky中并设置1.导入外部三维模型2.打开3D视图3.添加颗粒入口界面4.添加颗粒并设置属性5.设置颗粒与墙壁的碰撞属性6.设置颗粒入口流量7.求解 三、动画序列设置并导出 前言 刚开始学习离散元软件…

MyBatis 反射工具箱:带你领略不一样的反射设计思路

反射是 Java 世界中非常强大、非常灵活的一种机制。在面向对象的 Java 语言中&#xff0c;我们只能按照 public、private 等关键字的规范去访问一个 Java 对象的属性和方法&#xff0c;但反射机制可以让我们在运行时拿到任何 Java 对象的属性或方法。 有人说反射打破了类的封装…