shared_sharedpreferences - CSDN
精华内容
参与话题
  • 简单实现shared_ptr

    2019-11-27 12:21:52
    引言

    引言

    这是一个简单的实现shared_ptr的过程 因为是小练习的缘故 其中有些地方逻辑可能并不严密 希望大家指正

    注意点
    1. 删除器 因为shared_ptr的删除器是运行时绑定的 所以其类型应该是一个指针 所以我们需要一个函数指针 指向删除器
    2. 类的类型 这是一个典型的类指针的类 有共用一个指针 其实使用智能指针存储是最优的 但是我们就是在实现智能指针嘛 所以就操作普通指针就好
    #include<iostream>
    #include<string>
    #include<set>
    #include<map>
    #include<vector>
    #include<algorithm>
    #include<memory>
    #include<cstring>
    using namespace std;
    
    template <typename T> class Shared_ptr;
    
    template<typename T,typename ... Args>
    inline Shared_ptr<T> 
    make_Shared(Args&& ... args){
        return Shared_ptr<int>(std::forward<Args>(args)...);
    }
    //这里只是简单的实现而已 其中并没有涉及到内存的分配 也就使得第一个参数有点浪费
    
    template <typename T>
    class Shared_ptr
    {
        private:    
            size_t *Ptr_Count;//引用计数
            T* Mem_Ptr;//数据
            void (*Del_Ptr)(T *); //删除器 用于在运行时绑定删除器
        public:
            decltype(Del_Ptr) Return_Del(){return Del_Ptr;}
            //默认构造函数
            Shared_ptr():Ptr_Count(nullptr),Mem_Ptr(nullptr),Del_Ptr(nullptr){}
            
            //接收一个参数的构造函数(防止隐性转换)
            explicit Shared_ptr(T *tmp_ptr) try:
            Mem_Ptr(tmp_ptr),Ptr_Count(new size_t(1)),Del_Ptr(nullptr)
            {} catch(const std::bad_alloc &err) {cout << "error in new\n";}
            
            //不同类型转换
            template<class type>
            Shared_ptr(const Shared_ptr<type>& ptr) : Mem_Ptr(ptr->get()),Del_Ptr(ptr->Return_Del()),Ptr_Count(++(ptr->use_count())){}
    
            //拷贝构造函数
            Shared_ptr(Shared_ptr &tmp_ptr) noexcept(false){ //模板内部可简化为类名而不需要加类型
                Mem_Ptr = tmp_ptr.Mem_Ptr;
                Ptr_Count = tmp_ptr.Ptr_Count;
                Del_Ptr = nullptr;
                ++*Ptr_Count;
            }
            //拷贝赋值运算符
            void operator=(const Shared_ptr& tmp_ptr) & noexcept(false){
                Shared_ptr(std::move(tmp_ptr));//并没有什么数据成员 所以效率并没有什么提升 练下手
            }
    
            //不同类型转换
            template<class type>
            void operator=(const Shared_ptr<type>& tmp_ptr) & noexcept(false){
                Del_Ptr = tmp_ptr->Return_Del();
                Ptr_Count = ++(tmp_ptr->use_count());
                Mem_Ptr = tmp_ptr->get();
            }
    
            //移动构造函数
            Shared_ptr(Shared_ptr && tmp_ptr) noexcept:
            Mem_Ptr(tmp_ptr.Mem_Ptr),Ptr_Count(tmp_ptr.Ptr_Count),Del_Ptr(nullptr)
            {++*Ptr_Count;}
    
            //移动赋值运算符 //强制等号左边为左值
            void operator=(Shared_ptr && ptr) & noexcept{
                Shared_ptr(std::move(ptr));
            }
    
            //重载解引用运算符
            T& operator*(){
                return *Mem_Ptr;
            }
            T* operator->(){//至于为什么返回一个指针 因为Shared_ptr是一个类 类就应该返回一个成员
                return Mem_Ptr;
            } 
            T* get(){ //返回智能指针所保存的指针 操作危险
                return Mem_Ptr;
            }
            size_t use_count(){
                return *Ptr_Count;
            }
            bool unique(){
                return *Ptr_Count == 1;
            }
            void swap(Shared_ptr &tmp_ptr){
                //测试时使用原版本swap
                //std::swap(*this,tmp_ptr); 这里概念还有问题 搞懂move原理以后再来
    
                //交换指针效率更高
                std::swap(Mem_Ptr,tmp_ptr.Mem_Ptr);
                std::swap(Ptr_Count,tmp_ptr.Ptr_Count);
                std::swap(Del_Ptr,tmp_ptr.Del_Ptr);
            }
            void reset(){ //当reset为空时 只是把reset的指针
                if(*Ptr_Count == 1){
                    delete Mem_Ptr;
                    *Ptr_Count = 0; //防止析构时两次delete 用Ptr_count在析构时进行特判
                }else {
                    Mem_Ptr = nullptr;
                    --*Ptr_Count;
                }
            }
            void reset(T *tmp_ptr){
                if(*Ptr_Count==1){ //本对象是唯一对象则会释放   //不唯一的话其他对象还需要这两个指针
                    Del_Ptr ? Del_Ptr(Mem_Ptr) : delete Mem_Ptr;
                    delete Ptr_Count;
                }else{
                    --*Ptr_Count; 
                }
                Mem_Ptr = tmp_ptr;
                Ptr_Count = new size_t(1);//看起来很奇怪 仔细想想 如果count不为零的话其他智能指针还要使用
            }
            //接收一个指针和一个自定义的删除器 用于在析构时正确释放对象 默认为delete
            void reset(T *tmp_ptr,void (*del_ptr)(T *)){
                if(*Ptr_Count==1){
                    delete Mem_Ptr;
                    delete Ptr_Count;
                }
                --(*Ptr_Count);
                Mem_Ptr = tmp_ptr;
                Ptr_Count = new size_t(1);
                Del_Ptr = del_ptr; //定义一个删除器
            }
            ~Shared_ptr(){
                try{
                    cout << "Commen complete the destructor.\n";
    
                    if(Mem_Ptr == nullptr && Ptr_Count == nullptr){ //防止默认构造出现对仅有delete 而无new
                        return;
                    }
    
                    if((*Ptr_Count) == 0){ //证明被reset了 特殊处理
                        delete Ptr_Count;
                        Ptr_Count = nullptr;
                        return;
                    }
                    --*Ptr_Count;
                    if(*Ptr_Count==0){
                        Del_Ptr ? Del_Ptr(Mem_Ptr) : delete Mem_Ptr;
                        delete Ptr_Count;
                        Ptr_Count = nullptr;
                        Mem_Ptr = nullptr;
                    }
                }catch(...){
                    cout << "error in delete\n";
                }
            }
    };
    
    //一个测试用的聚合类
    struct text{
        int ans;
        int weight;
    };
    
    void Del(text* tmp){//聚合类 text 的删除器
        delete tmp;
        cout << "Special complete the destructor.\n";
    }
    
    //demo
    int main()
    {
        cout << "测试make_shared:\n";
        Shared_ptr<int> str=make_Shared<int>(new int(5));
        cout << ": " << *str << endl;
        cout << "测试基本操作\n";
        Shared_ptr<int> tmpa_ptr(new int(10));
        cout << *tmpa_ptr << endl;
        cout << tmpa_ptr.use_count() << endl;
        Shared_ptr<int> tmpb_ptr(tmpa_ptr);
        cout << *tmpb_ptr << endl;
        cout << tmpb_ptr.use_count() << endl;
        
        cout << "交换操作\n";
        Shared_ptr<int> tmp_swap(new int(5));
        cout << *tmpa_ptr << " " << *tmp_swap << endl;
        tmpa_ptr.swap(tmp_swap);
        cout << *tmpa_ptr << " " << *tmp_swap << endl;
    
        cout << "测试三种参数的reset函数\n";
        tmpa_ptr.reset();
        cout << tmpa_ptr.use_count() << " " << tmpb_ptr.use_count() << endl;
    
        shared_ptr<text> tmpc_ptr(new text({5,5}));
        cout << (*tmpc_ptr).ans << " " << (*tmpc_ptr).weight << endl;
        tmpc_ptr.reset(new text({10,10}));
        cout << (*tmpc_ptr).ans << " " << (*tmpc_ptr).weight << endl;
        tmpc_ptr.reset(new text({20,20}),Del);//shared_ptr删除器运行时绑定 所以使用指针存储删除器
    
        cout << "测试get函数\n";
        text *temp_text_ptr = tmpc_ptr.get(); //此函数小心使用 会返回智能指针所维护的指针域
        cout << temp_text_ptr->ans << " " << temp_text_ptr->weight << endl;
    
        cout << "开始析构\n";
    
        return 0;
    }
    

    这是测试代码的输出

    测试make_shared:
    : 5
    测试基本操作
    10
    1
    10
    2
    交换操作
    10 5
    5 10
    测试三种参数的reset函数
    0 2
    5 5
    10 10
    测试get函数
    20 20
    开始析构
    Special complete the destructor.
    Commen complete the destructor.
    Commen complete the destructor.
    Commen complete the destructor.
    Commen complete the destructor.
    

    在本篇博客中只是简单实现了make_shared 就像我们注释中所说的 这样的写法导致make_shard的第一个参数有些多余 但实际一定不是这样的 我们来看看make_shared的源码

      template<typename _Tp, typename... _Args>
        inline shared_ptr<_Tp>
        make_shared(_Args&&... __args)
        {
          typedef typename std::remove_const<_Tp>::type _Tp_nc;
          return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
    				       std::forward<_Args>(__args)...);
        }
    

    我们可以看到第一个类型参数的存在是很有必要的 为了能够正确的分配内存  我们这个模板函数也应该由我们显式的提供类型

    展开全文
  • Android 中 SharedPreferences 的简单用法试例

    万次阅读 多人点赞 2018-03-22 11:12:24
    SharedPreferences 是一个轻量级的存储类,主要是保存一些小的数据,一些状态信息实现:1、确定要保存的是什么数据后,写个 SharedPreferences 存储类2、获取到数据后,调用 SharedPreferences 存储类将数据保存3...

    SharedPreferences 是一个轻量级的存储类,主要是保存一些小的数据,一些状态信息

    实现:

    1、确定要保存的是什么数据后,写个 SharedPreferences  存储类

    2、获取到数据后,调用 SharedPreferences  存储类将数据保存

    3、调用 SharedPreferences  存储类将数据取出,并展示出来


    效果:

    获取数据保存


    获取数据展示



    实现详情:

    1、确定要保存的是什么数据后,写个 SharedPreferences  存储类

    我的构思是将注册时的信息保存在 SharedPreferences  中,在其他地方还需要用到

    注册信息有 :账号、密码、性别、年龄

    如果还有其他数据需要保存,也只要根据需求添加 get、set 方法就好了

    import android.content.Context;
    import android.content.SharedPreferences;
    
    /**
     * Created by Administrator on 2018/3/22 0022.
     * 保存一些简单的数据
     */
    
    public class MySharedPreferences {
    
        //创建一个SharedPreferences    类似于创建一个数据库,库名为 data
        public static SharedPreferences share(Context context){
            SharedPreferences sharedPreferences = context.getSharedPreferences("date", Context.MODE_PRIVATE);
            return sharedPreferences;
        }
    
        //name 账号
        //调用上面的 share(Context context) 方法 获取标识为 "name" 的数据
        public static Object getName(Context context){
            return share(context).getString("name",null);
        }
        //调用上面的 share(Context context) 方法 将数据存入,并标识为 "name"
        //使用 commit() 方法保存会给出反应(保存成功或失败)
        public static boolean setName(String name, Context context){
            SharedPreferences.Editor e = share(context).edit();
            e.putString("name",name);
            Boolean bool = e.commit();
            return bool;
        }
    
        //pswd 密码
        public static String getPswd(Context context){
            return share(context).getString("pswd",null);
        }
        //这里使用的是 apply() 方法保存,将不会有返回值
        public static void setPswd(String pswd, Context context){
            SharedPreferences.Editor e = share(context).edit();
            e.putString("pswd",pswd);
            e.apply();
        }
    
        /**
         * 可以根据需求选择用那种方式保存数据
         * (需不需要告诉你有没有保存成功)
         */
    
        //sex 性别
        public static String getSex(Context context){
            return share(context).getString("sex",null);
        }
        public static void setSex(String sex, Context context){
            SharedPreferences.Editor e = share(context).edit();
            e.putString("sex",sex);
            e.apply();
        }
    
        //age 年龄
        public static String getAge(Context context){
            return share(context).getString("age",null);
        }
        public static void setAge(String age, Context context){
            SharedPreferences.Editor e = share(context).edit();
            e.putString("age",age);
            e.apply();
        }
        
    }

    从上面可以看到,我创建了一个 MySharedPreferences  类,

    在类中创建了SharedPreferences  后,为每个需要存储的都写了 set 、get 方法

    通过 set 、 get方法来 保存和获取 需要的数据。

    同时,我的试例中有两种保存方法: commit() 和 apply()

    commit() 方法会返回保存状态(成功返回 true,失败返回 false)

    apply() 方法则没有任何返回

    可以根据需要自由使用


    2、获取到数据后,调用 SharedPreferences  存储类将数据保存

    第二步分为获取数据和保存数据

    获取数据:

    我这里是通过在 EditText 输入获取数据,其实数据来源可以根据自己需求来

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:orientation="vertical"
        tools:context="com.win.sharedprefences.MainActivity">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="#000000"
            android:text="保存数据"/>
    
        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="账号:"/>
    
        <EditText
            android:id="@+id/pswd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密码:"/>
    
        <EditText
            android:id="@+id/sex"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="性别:"/>
    
        <EditText
            android:id="@+id/age"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="年龄:"/>
    
        <Button
            android:id="@+id/submit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="提交保存"/>
        
    </LinearLayout>
    这个界面就是上面展示的“获取数据保存”界面

    保存数据:

    通过用户输入,我们获取到了将要保存起来的数据,接下来我们把它们保存起来

    第一步在 MySharedPreferences类 定义了保存数据的 set方法,调用就好了

    //点击事件(获取数据,存入SharedPreferences)
    submit.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //获取输入框的内容
            String names = name.getText().toString();
            String pswds = pswd.getText().toString();
            String sexs = sex.getText().toString();
            String ages = age.getText().toString();
    
            //在 MySharedPreferences类里定义好存取方法后,就可以调用了
            //这里将数据保存进去  注意:(name 我是定义了有返回值的,试试看)
            Boolean bool = MySharedPreferences.setName(names, MainActivity.this);
            MySharedPreferences.setPswd(pswds, MainActivity.this);
            MySharedPreferences.setSex(sexs,   MainActivity.this);
            MySharedPreferences.setAge(ages,   MainActivity.this);
    
            //看看保存成功没
            if(bool)
                Toast.makeText(MainActivity.this, "保存成功!", Toast.LENGTH_SHORT).show();
            else
                Toast.makeText(MainActivity.this, "保存失败!", Toast.LENGTH_SHORT).show();
    
            //跳转到展示界面
            startActivity(new Intent(MainActivity.this, Main2Activity.class));
        }
    });
    这里就展示核心就好了


    3、调用 SharedPreferences  存储类将数据取出,并展示出来

    从第二步的代码中可以看到,当点击了保存数据按钮后,就会跳到展示界面去

    展示界面就是上面效果中的“获取数据展示”所示

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:orientation="vertical"
        tools:context="com.win.sharedprefences.Main2Activity">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="#000000"
            android:text="使用数据"/>
    
        <Button
            android:id="@+id/obtain"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="获取数据"/>
    
        <TextView
            android:id="@+id/one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/two"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/three"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/four"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>

    布局大家应该都会

    我的定义是,点击展示按钮就获取数据,将数据展示在 TextView 中

    获取数就调用 MySharedPreferences类中定义好的 get 方法

    //获取保存好的数据并展示出来
    obtain.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //通过MySharedPreferences类里定义好的存取方法,获取保存在里面的数据
            String names = (String) MySharedPreferences.getName(Main2Activity.this);
            String pswds = (String) MySharedPreferences.getPswd(Main2Activity.this);
            String sexs =  (String)  MySharedPreferences.getSex(Main2Activity.this);
            String ages =  (String)  MySharedPreferences.getAge(Main2Activity.this);
    
            //将数据放到 TextView 展示出来
            one.setText("账号:" + names);
            two.setText("密码:" + pswds);
            three.setText("性别:" + sexs);
            four.setText("年龄:" + ages);
        }
    });


    注释: SharedPreferences 的使用可以根据自己需求灵活运用,

                里面保存的值需要改变的时候,在调用一下 set 方法就行了

                同样的标识会被覆盖掉


    源码:https://github.com/iscopy/SharedPreferences


    展开全文
  • SharedPreference使用

    千次阅读 2018-07-03 15:07:51
    SharedPreferences简介 为了保存软件的设置参数,Android...使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下。 一.获取到应用中的SharedPreferences的...

    SharedPreferences简介
    为了保存软件的设置参数,Android平台为我们提供了一个SharedPreferences接口,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下。


    一.获取到应用中的SharedPreferences的两种方式

    • getSharedPreferences()

      如果需要多个通过名称参数来区分的shared preference文件, 名称可以通过第一个参数来指定。可在app中通过任何一个Context 执行该方法。

    SharedPreferences sharedPref = context.getSharedPreferences(
            getString(R.string.preference_file_key), Context.MODE_PRIVATE);

    mode指定为MODE_PRIVATE,则该配置文件只能被自己的应用程序访问。
    mode指定为MODE_WORLD_READABLE,则该配置文件除了自己访问外还可以被其它应该程序读取。
    mode指定为MODE_WORLD_WRITEABLE,则该配置文件除了自己访问外还可以被其它应该程序读取和写入

    • getPreferences()

    这个方法默认使用当前类不带包名的类名作为文件的名称,同时也和上面一样传入文件读写的模式。

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

    二.读写SharedPreferences

    • 写SharedPreferences

    为了写shared preferences文件,需要通过执行edit()创建一个 SharedPreferences.Editor
    通过类似putInt()与putString()等方法传递keys与values,接着通过commit() 提交改变.

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putInt(getString(R.string.saved_high_score), newHighScore);
    editor.commit();
    • 读SharedPreferences

    为了从shared preference中读取数据,可以通过类似于 getInt() 及 getString()等方法来读取。在那些方法里面传递我们想要获取的value对应的key,并提供一个默认的value作为查找的key不存在时函数的返回值。如下:

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
    long highScore = sharedPref.getInt(getString(R.string.saved_high_score), default);

    三.系统默认的SharedPreferences
    每个应用有一个默认的偏好文件preferences.xml,使用getDefaultSharedPreferences获取,其余操作是一样的。

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    SharedPreferences.Editor editor = preferences.edit();
    editor.putBoolean("if_set_location", false);
    editor.commit();

    四.访问其他应用的SharedPreferences
    如果应用B要读写访问A应用中的Preference前提条件,A应用中该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限,代表其他的应用能访问读取或者写入。
    具体步骤:
    1.在B中创建一个指向A应用的Context

    Context otherAppsContext = createPackageContext("A应用的包名", Context.CONTEXT_IGNORE_SECURITY);

    2.再通过context获取到SharedPreferences实体:

    SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("SharedPreferences的文件    名", Context.MODE_WORLD_READABLE);
    String name = sharedPreferences.getString("key", "");

    注:如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:

    File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);
    //<package name>应替换成应用的包名。
    展开全文
  • 一篇文章彻底理解SharedPreferences

    万次阅读 2018-07-01 21:01:14
    进行映射,最终会在手机的/data/data/package_name/shared_prefs/目录下以xml的格式存在。 Sp通常用于记录一些参数配置、行为标记等!因为其使用简单,所以大多数开发者用起来很爽!但是 请注意:千万不要使用Sp去...

    一、概述

    SharedPreferences简称Sp(后面都会称Sp),是一种轻量级的数据存储方式,采用Key/value的方式
    进行映射,最终会在手机的/data/data/package_name/shared_prefs/目录下以xml的格式存在。
    Sp通常用于记录一些参数配置、行为标记等!因为其使用简单,所以大多数开发者用起来很爽!但是
    请注意:千万不要使用Sp去存储量大的数据,也千万不要去让你的Sp文件超级大,否则会大大影响应用性能,
    甚至出现ANR,没错是ANR(下面会分析)。

    二、使用分析

    1.获取一个SharedPreferences
    /**
    *param:name在/data/data/package_name/shared_prefs/目录下生成的文件的名字(如果该文件不存在就会创建,如果存在则更新)
    *param:mode该文件的访问模式(Context.MODE_PRIVATE:默认的创建模式,只能由创建它的或者UID相同的应用程序访问,其余三种已经废弃,故不介绍)
    *传送门:https://developer.android.com/reference/android/content/Context.html#MODE_PRIVATE
    */
    SharedPreferences sharedPreferences = getSharedPreferences(NAME_SP_TEST, Context.MODE_PRIVATE);

    2.通过Eidt更新数据

    传送门:https://developer.android.com/reference/android/content/SharedPreferences.Editor
    /**
    *获取一个Edit对象,所有对数据的操作都需要经过Edit
    */
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putBoolean(“Lbjfan”,true);

    /**
    *apply和commit是两种不同的写入方式(源码会说到),这时候更新才会生效(有则添加,无则更新)
    */
    editor.apply();//editor.commit();

    三.源码分析

    1.getSharedPreferences();

     public SharedPreferences getSharedPreferences(String name, int mode) {
         Class var4 = ContextImpl.class;
         SharedPreferencesImpl sp;
         synchronized(ContextImpl.class) {
             //一个全局的静态对象,定义如下:private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
             if (sSharedPrefs == null) {
                 sSharedPrefs = new ArrayMap();
             }
             //根据应用程序的包名在程序的执行环境中取到该应用程序对应的SharedPreferences文件组
             String packageName = this.getPackageName();
             ArrayMap<String, SharedPreferencesImpl> packagePrefs = (ArrayMap)sSharedPrefs.get(packageName);
             //如果不存在就创建,并缓存到全局的ArrayMap(sSharedPrefs)中
             if (packagePrefs == null) {
                 packagePrefs = new ArrayMap();
                 sSharedPrefs.put(packageName, packagePrefs);
             }
    
             if (this.mPackageInfo.getApplicationInfo().targetSdkVersion < 19 && name == null) {
                 name = "null";
             }
            //根据文件的Name去拿到对应的SharedPreferencesImpl对象,如果没有就创建,并放入到自己的上一级缓存packagePrefs中
             sp = (SharedPreferencesImpl)packagePrefs.get(name);
             if (sp == null) {
                 File prefsFile = this.getSharedPrefsFile(name);
                 sp = new SharedPreferencesImpl(prefsFile, mode);
                 packagePrefs.put(name, sp);
                 return sp;
             }
         }
    
         if ((mode & 4) != 0 || this.getApplicationInfo().targetSdkVersion < 11) {
             sp.startReloadIfChangedUnexpectedly();
         }
    
         return sp;
     }
    

    总结起来就是:由于ContextImpl是应用程序的执行环境,每个应用程序里面可以包含有多个SharedPreference文件。因此,为了更好的定位SharedPreference文件,首先根据应用程序进行筛选,得到ArrayMap 然后再通过SharedPreference文件名进行筛选,得到SharedPreferencesImpl。可以看到,SharedPreferencesImpl只会被创建一次,之后会被保存在缓存中,后续的获取操作都是从缓存中获取SharedPreferencesImpl实例对象。

    获取文件目录的函数,注意到shared_prefs这个关键词

     private File getPreferencesDir() {
        Object var1 = this.mSync;
        synchronized(this.mSync) {
            if (this.mPreferencesDir == null) {
                this.mPreferencesDir = new File(this.getDataDirFile(), "shared_prefs");
            }
            return this.mPreferencesDir;
        }
    }

    2.SharedPreferencesImpl的构造函数
    构造函数主要进行文件备份,加载状态初始化等准备操作,并开始将文件从磁盘加载到内存

     SharedPreferencesImpl(File file, int mode) {
        this.mFile = file;
        //同名的.bak文件用于当发生异常时,用于恢复数据
        this.mBackupFile = makeBackupFile(file);
        this.mMode = mode;
        this.mLoaded = false;
        this.mMap = null;
        this.startLoadFromDisk();
    }

    从磁盘读取文件

    private void startLoadFromDisk() {
        //同步一个标记位,是否已经从磁盘读取文件,读取到以后就不用再去读取
        synchronized (mLock) {
            mLoaded = false;
        }
        //开启一个子线程去从磁盘加载文件,文件读取属于耗时操作,主线程容易ANR
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                loadFromDisk();
            }
        }.start();
    }

    3.具体读文件的操作

    private void loadFromDisk() {
            synchronized (mLock) {
                //保证只加载一次
                if (mLoaded) {
                    return;
                }
                //如果备份文件已经存在了,则删除当前文件,用备份文件来代替
                if (mBackupFile.exists()) {
                    mFile.delete();
                    mBackupFile.renameTo(mFile);
                }
            }
    
            // Debugging
            if (mFile.exists() && !mFile.canRead()) {
                Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
            }
    
            Map<String, Object> map = null;
            StructStat stat = null;
            Throwable thrown = null;
            //具体的文件读取操作,然后解析XML,存储在Map中!这也是为何SharedPrefernces不是线程安全的,因为使用的是Map数据结构
            try {
                stat = Os.stat(mFile.getPath());
                if (mFile.canRead()) {
                    BufferedInputStream str = null;
                    try {
                        str = new BufferedInputStream(
                                new FileInputStream(mFile), 16 * 1024);
                        map = (Map<String, Object>) XmlUtils.readMapXml(str);
                    } catch (Exception e) {
                        Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
                    } finally {
                        IoUtils.closeQuietly(str);
                    }
                }
            } catch (ErrnoException e) {
                // An errno exception means the stat failed. Treat as empty/non-existing by
                // ignoring.
            } catch (Throwable t) {
                thrown = t;
            }
    
            synchronized (mLock) {
                //将mLoaded置为空,让等待加载完成的线程被唤醒
                mLoaded = true;
                mThrowable = thrown;
    
                // It's important that we always signal waiters, even if we'll make
                // them fail with an exception. The try-finally is pretty wide, but
                // better safe than sorry.
                try {
                    if (thrown == null) {
                        if (map != null) {
                            保存加载内容到mMap中
                            mMap = map;
                            mStatTimestamp = stat.st_mtim;
                            mStatSize = stat.st_size;
                        } else {
                            mMap = new HashMap<>();
                        }
                    }
                    // In case of a thrown exception, we retain the old map. That allows
                    // any open editors to commit and store updates.
                } catch (Throwable t) {
                    mThrowable = t;
                } finally {
                    //唤醒被阻塞的线程,
                    mLock.notifyAll();
                }
            }
        }

    4.数据获取

    public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            //等待文件加载到内存
            awaitLoadedLocked();
            //直接从内存中的Map中读取
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }
    private void awaitLoadedLocked() {
        if (!mLoaded) {
            // Raise an explicit StrictMode onReadFromDisk for this
            // thread, since the real read will be in a different
            // thread and otherwise ignored by StrictMode.
            BlockGuard.getThreadPolicy().onReadFromDisk();
        }
        //等待文件记载到内存
        while (!mLoaded) {
            try {
                //线程Wait阻塞
                mLock.wait();
            } catch (InterruptedException unused) {
            }
        }
        if (mThrowable != null) {
            throw new IllegalStateException(mThrowable);
        }
    }

    小结:一般情况下使用Sp获取数据操作的操作都是在主线程进行的,如果第一次获取数据的时候,Sp文件过大,那么子线程从磁盘获取文件的操作比较耗时,导致主线程等待的时间大于5秒,此时就会发生ANR

    5.数据更新

    SharedPreferences.Editor editor = sharedPreferences.edit();//Edit的实现类EditorImpl
    editor.putString();
    editor.apply() or editor.commit()

    构造方法

    public Editor edit() {
            //
            synchronized (mLock) {
                //同样使调用Edit的线程等待,如果我们在主线程中切Sp文件没有被加载到内存中也会造成阻塞,引发ANR
                awaitLoadedLocked();
            }
    
            return new EditorImpl();
    }

    put方法

    public Editor putString(String key, @Nullable String value) {
        synchronized (this) {
            //更新到内存中
            mModified.put(key, value);
            return this;
        }
    }

    apply和commit方法
    commit方法(有返回值,告诉开发者成功还是失败)

    public boolean commit() {
        long startTime = 0;
    
        if (DEBUG) {
            startTime = System.currentTimeMillis();
        }
        //构建需要写入的数据
        MemoryCommitResult mcr = commitToMemory();
    
        SharedPreferencesImpl.this.enqueueDiskWrite(
            mcr, null /* sync write on this thread okay */);
        try {
            //等待写入结果,主线程调用,写入时间过长会导致阻塞,发生ANR
            mcr.writtenToDiskLatch.await();
        } catch (InterruptedException e) {
            return false;
        } finally {
            if (DEBUG) {
                Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                        + " committed after " + (System.currentTimeMillis() - startTime)
                        + " ms");
            }
        }
        //观察者模式通知写入完成
        notifyListeners(mcr);
        //返回写入结果
        return mcr.writeToDiskResult;
    }

    构建需要写入的数据

    private MemoryCommitResult commitToMemory() {
                long memoryStateGeneration;
                List<String> keysModified = null;
                Set<OnSharedPreferenceChangeListener> listeners = null;
                Map<String, Object> mapToWriteToDisk;
    
                synchronized (SharedPreferencesImpl.this.mLock) {
                    // We optimistically don't make a deep copy until
                    // a memory commit comes in when we're already
                    // writing to disk.
                    if (mDiskWritesInFlight > 0) {
                        // We can't modify our mMap as a currently
                        // in-flight write owns it.  Clone it before
                        // modifying it.
                        // noinspection unchecked
                        //将内存中的数据进行深拷贝
                        mMap = new HashMap<String, Object>(mMap);
                    }
                    //讲内存中Map的值赋给需要写入文件的Map
                    mapToWriteToDisk = mMap;
                    //写入文件的次数
                    mDiskWritesInFlight++;
    
                    boolean hasListeners = mListeners.size() > 0;
                    if (hasListeners) {
                        keysModified = new ArrayList<String>();
                        listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
                    }
    
                    synchronized (mEditorLock) {
                        boolean changesMade = false;
    
                        if (mClear) {
                            if (!mapToWriteToDisk.isEmpty()) {
                                changesMade = true;
                                mapToWriteToDisk.clear();
                            }
                            mClear = false;
                        }
                        //对Key/Value的处理,有则更新,无则添加
                        for (Map.Entry<String, Object> e : mModified.entrySet()) {
                            String k = e.getKey();
                            Object v = e.getValue();
                            // "this" is the magic value for a removal mutation. In addition,
                            // setting a value to "null" for a given key is specified to be
                            // equivalent to calling remove on that key.
                            if (v == this || v == null) {
                                //如果已经存在相同的key/value,则直接返回,否则添加到需要写入磁盘的map中
                                if (!mapToWriteToDisk.containsKey(k)) {
                                    continue;
                                }
                                mapToWriteToDisk.remove(k);
                            } else {
                                if (mapToWriteToDisk.containsKey(k)) {
                                    Object existingValue = mapToWriteToDisk.get(k);
                                    if (existingValue != null && existingValue.equals(v)) {
                                        continue;
                                    }
                                }
                                mapToWriteToDisk.put(k, v);
                            }
    
                            changesMade = true;
                            if (hasListeners) {
                                keysModified.add(k);
                            }
                        }
    
                        mModified.clear();
    
                        if (changesMade) {
                            mCurrentMemoryStateGeneration++;
                        }
    
                        memoryStateGeneration = mCurrentMemoryStateGeneration;
                    }
                }
                return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
                        mapToWriteToDisk);
            }

    创建并开启一个线程写入磁盘

    private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                              final Runnable postWriteRunnable) {
                    final boolean isFromSyncCommit = (postWriteRunnable == null);
    
                    final Runnable writeToDiskRunnable = new Runnable() {
                            @Override
                            public void run() {
                                synchronized (mWritingToDiskLock) {
                                    //将数据通过文件写入磁盘
                                    writeToFile(mcr, isFromSyncCommit);
                                }
                                synchronized (mLock) {
                                   //同时写入磁盘的计数-1
                                    mDiskWritesInFlight--;
                                }
                                if (postWriteRunnable != null) {
                                    postWriteRunnable.run();
                                }
                            }
                        };
    
                    // Typical #commit() path with fewer allocations, doing a write on
                    // the current thread.
                    //同步写入操作
                    if (isFromSyncCommit) {
                        boolean wasEmpty = false;
                        synchronized (mLock) {
                            //如果之前的写入操作都执行完毕,则直接写入
                            wasEmpty = mDiskWritesInFlight == 1;
                        }
                        if (wasEmpty) {
                            writeToDiskRunnable.run();
                            return;
                        }
                    }
                    //如果之前的写入操作没有完成,则加入同步等待队列
                    QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
                }

    写入文件的操作

    private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
                    long startTime = 0;
                    long existsTime = 0;
                    long backupExistsTime = 0;
                    long outputStreamCreateTime = 0;
                    long writeTime = 0;
                    long fsyncTime = 0;
                    long setPermTime = 0;
                    long fstatTime = 0;
                    long deleteTime = 0;
    
                    if (DEBUG) {
                        startTime = System.currentTimeMillis();
                    }
    
                    boolean fileExists = mFile.exists();
    
                    if (DEBUG) {
                        existsTime = System.currentTimeMillis();
    
                        // Might not be set, hence init them to a default value
                        backupExistsTime = existsTime;
                    }
    
                    // Rename the current file so it may be used as a backup during the next read
                    if (fileExists) {
                        boolean needsWrite = false;
    
                        // Only need to write if the disk state is older than this commit
                        // 只有磁盘上文件状态比当前文件更旧时,才执行更新操作
                        if (mDiskStateGeneration < mcr.memoryStateGeneration) {
                            if (isFromSyncCommit) {
                                needsWrite = true;
                            } else {
                                synchronized (mLock) {
                                    // No need to persist intermediate states. Just wait for the latest state to
                                    // be persisted.
                                    if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
                                        needsWrite = true;
                                    }
                                }
                            }
                        }
                        // 不需要立即写入,则在MemoryCommitResult中记录该结果,然后直接返回
                        if (!needsWrite) {
                            mcr.setDiskWriteResult(false, true);
                            return;
                        }
    
                        boolean backupFileExists = mBackupFile.exists();
    
                        if (DEBUG) {
                            backupExistsTime = System.currentTimeMillis();
                        }
    
                        if (!backupFileExists) {
                            if (!mFile.renameTo(mBackupFile)) {
                                Log.e(TAG, "Couldn't rename file " + mFile
                                      + " to backup file " + mBackupFile);
                                mcr.setDiskWriteResult(false, false);
                                return;
                            }
                        } else {
                            mFile.delete();
                        }
                    }
    
                    // Attempt to write the file, delete the backup and return true as atomically as
                    // possible.  If any exception occurs, delete the new file; next time we will restore
                    // from the backup.
                    // 当尝试写入文件时,删除备份文件,并返回true。如果在写入过程中发生了异常,则删除新的文件,下一次从备份文件中恢复。
                    try {
                        FileOutputStream str = createFileOutputStream(mFile);
    
                        if (DEBUG) {
                            outputStreamCreateTime = System.currentTimeMillis();
                        }
    
                        if (str == null) {
                            mcr.setDiskWriteResult(false, false);
                            return;
                        }
                        XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
    
                        writeTime = System.currentTimeMillis();
    
                        FileUtils.sync(str);
    
                        fsyncTime = System.currentTimeMillis();
    
                        str.close();
                        ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
    
                        if (DEBUG) {
                            setPermTime = System.currentTimeMillis();
                        }
    
                        try {
                            final StructStat stat = Os.stat(mFile.getPath());
                            synchronized (mLock) {
                                mStatTimestamp = stat.st_mtim;
                                mStatSize = stat.st_size;
                            }
                        } catch (ErrnoException e) {
                            // Do nothing
                        }
    
                        if (DEBUG) {
                            fstatTime = System.currentTimeMillis();
                        }
    
                        // Writing was successful, delete the backup file if there is one.
                        mBackupFile.delete();
    
                        if (DEBUG) {
                            deleteTime = System.currentTimeMillis();
                        }
    
                        mDiskStateGeneration = mcr.memoryStateGeneration;
                        //记录写入成功了,唤醒等待写入结果的线程
                        mcr.setDiskWriteResult(true, true);
    
                        if (DEBUG) {
                            Log.d(TAG, "write: " + (existsTime - startTime) + "/"
                                    + (backupExistsTime - startTime) + "/"
                                    + (outputStreamCreateTime - startTime) + "/"
                                    + (writeTime - startTime) + "/"
                                    + (fsyncTime - startTime) + "/"
                                    + (setPermTime - startTime) + "/"
                                    + (fstatTime - startTime) + "/"
                                    + (deleteTime - startTime));
                        }
    
                        long fsyncDuration = fsyncTime - writeTime;
                        mSyncTimes.add((int) fsyncDuration);
                        mNumSync++;
    
                        if (DEBUG || mNumSync % 1024 == 0 || fsyncDuration > MAX_FSYNC_DURATION_MILLIS) {
                            mSyncTimes.log(TAG, "Time required to fsync " + mFile + ": ");
                        }
    
                        return;
                    } catch (XmlPullParserException e) {
                        Log.w(TAG, "writeToFile: Got exception:", e);
                    } catch (IOException e) {
                        Log.w(TAG, "writeToFile: Got exception:", e);
                    }
    
                    // Clean up an unsuccessfully written file
                    if (mFile.exists()) {
                        if (!mFile.delete()) {
                            Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
                        }
                    }
                    mcr.setDiskWriteResult(false, false);
                }

    在将writeToFile()方法中,首先将当前文件重命名为备份文件,然后从当前文件中获取文件输出流,并将MemoryCommitResult中保存的备份数据,写入到文件输出流中。如果写入成功,则删除备份文件,返回真。如果写入失败,则删除当前文件,下一次从备份文件中恢复过来。

    通知调用者是否写入成功是通过setDiskWriteResult()方法来完成的,在该方法中,通过MemoryCommitResult的writeToDiskResult变量来保存写入结果,写入成功为真,写入失败为假。不管写入成功还是失败,都会让writtenToDiskLatch闭锁计数减1,唤醒在闭锁上等待的线程。

    因此,整个commit操作可以概括为:
    1.创建写入数据
    2.如果当前没有写入操作,则通过线程直接写入,如果有则添加到单任务队列中等待
    3.保存写入结果,并唤醒等待写入的线程
    4.通知所有的监听者写入完成
    5.返回给调用这写入结果

    apply操作

    public void apply() {
        final long startTime = System.currentTimeMillis();
        //构建需要写入的数据
        final MemoryCommitResult mcr = commitToMemory();
        final Runnable awaitCommit = new Runnable() {
                @Override
                public void run() {
                    try {
                        //等待写入完成
                        mcr.writtenToDiskLatch.await();
                    } catch (InterruptedException ignored) {
                    }
    
                    if (DEBUG && mcr.wasWritten) {
                        Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                                + " applied after " + (System.currentTimeMillis() - startTime)
                                + " ms");
                    }
                }
            };
        //添加到等待任务队列
        QueuedWork.addFinisher(awaitCommit);
    
        Runnable postWriteRunnable = new Runnable() {
                @Override
                public void run() {
                    //异步等待
                    awaitCommit.run();
                    //从等待任务队列移除
                    QueuedWork.removeFinisher(awaitCommit);
                }
            };
        //与commit方法不同,通过postWriteRunnable异步等待
        SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
    
        // Okay to notify the listeners before it's hit disk
        // because the listeners should always get the same
        // SharedPreferences instance back, which has the
        // changes reflected in memory.
        notifyListeners(mcr);
    }

    综上:commit同步写入,有返回值,但是会造成调用它的线程阻塞,apply异步写入,无返回值!

    四.Sp滥用带来的性能问题

    1.第一次读取时,造成主线程阻塞(文件过大,主线程等待时间过长),引起ANR
    2.文件太大,加载到内存中一直存在,占用大量内存
    3.直接在主线程进行commit操作,造成阻塞,引发ANR
    4.多次apply,造成锁竞争,浪费系统资源
    5.Sp文件过大,每次更新都更新整个文件

    五.Sp最佳实践方案

    1.对Sp进行合适的拆分
    2.在合适的时机进行异步初始化
    3.批量修改一次提交
    4.在主线程谨慎使用commit

    SharedPreferences作为一种轻量级的数据存储方式,极大的方便了我们的操作!但请一定谨慎的使用!

    展开全文
  • WPF X:Shared概述

    2017-05-25 16:39:51
    X:Shared用于指定请求资源时创建实例的两种方式。 X:Shared = “true”(默认):表示所有请求都是共享同一个实例。一般不显示指定。 X:Shared = “false”:表示每次请求都创建一个新的实例。 二、使用 1...
  • Android之SharedPreferences详解

    万次阅读 2016-01-21 11:10:16
    SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对)SharedPreferences常用来存储一些轻量级的数据。 1、使用SharedPreferences保存数据方法如下: ...
  • shared_ptr与make_shared的用法

    万次阅读 2017-05-13 13:28:24
    程序使用动态内存出于以下三种原因之一 1、程序不知道自己需要多少对象; 2、程序不知道所需对象的准确类型;...eg: shared_ptr PointCloudDlgPointer;//指向类型为CDlgPointCloud的对象PointCloudDlgPointer;
  • error while loading shared libraries的解決方法

    万次阅读 多人点赞 2008-09-22 19:14:00
    在linux下运行程序时,发现了error while loading shared libraries这种错误,一时间不知道解决办法,在网上搜索,终于解决了: ./tests: error while loading shared libraries: xxx.so.0:cannot open shared ...
  • 原文转自,有改动一、问题运行hydra时,提示错误:hydra : error : while loading shared libraries: libssh.so.4: cannot open shared object file: No such file分析原因:链接器ld提示找不到库文件。ld默认的目录...
  • 解决OpenSSL:error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No 解决OpenSSL:error while loading shared libraries: libcrypto.so.1.1: cannot open shared...
  • 在安装mysql时遇到以下错误 ..../bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory 原因是没有安装libaio.so.1,安装即可。 Ub
  • 在初始化mysql5.7的时候,报以下错误error while loading shared libraries: libnuma.so.1: cannot open shared object file: No such file or directory备注: 初始化参数为/usr/local/mysql/bin/mysqld --...
  • 前提:ubuntu-debug机器上向SVN提交了pdu-IVT,想在别的普通机器上验证直接make能否成功,编译出的用户程序能否运行。 工作PC机上装有VMware,里面的ubuntu版本跟ubuntu-debug机器上相同,都是ubuntu 11.10版本...
  • std::shared_ptr 与普通指针的转换

    万次阅读 2017-09-18 14:42:07
    shared_ptr 是一个类,用模板支持很多类型。 shared_ptrshared_a(10); int *b=NULL; b = &*shared_a;//*share_a 拷贝shared_a里面值创建临时对象, 再&取得临时对象地址 b = shared_a.get();
  • boost::make_shared

    万次阅读 2013-09-25 11:04:05
    shared_ptr很好地消除了显式的delete调用,如果读者掌握了它的用法,可以肯定delete将会在你的编程字典中彻底消失。 但这还不够,因为shared_ptr的构造还需要new调用,这导致了代码中的某种不对称性。虽然shared_...
  • C++11使用make_shared的优势和劣势

    万次阅读 2017-06-06 19:26:11
    Make_shared Why Make_shared ? C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::shared_ptr 的构造函数相比它能给我们带来什么好处呢 ?...
  • root@bigdata-159:/usr/local/mysql# ./bin/mysqld -- defaults-file=/etc/my.cnf --initialize --user=mysql ..../bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file:
  • shared_ptr的头文件

    万次阅读 2016-04-06 00:08:46
    之前编译的时候一直无法识别shared_ptr,原来是头文件少了, 加上 这两个头文件就ok了。 #include #include
  • python 使用protobuf出错:protoc: error while loading shared libraries: libprotoc.so.9: cannot open shared object file:No such...解决方法:linux 敲击命令:export LD_LIBRARY_PATH=/usr/local/lib...
  • shared_ptr

    万次阅读 2011-02-10 16:36:00
    shared_ptr是一个最像指针的"智能指针",是boost.smart_ptr库中最有价值、最重要的组成部分,也是最有用的,Boost库的许多组件--甚至还包括其他一些领域的智能指针都使用了shared_ptr。抱歉,我实在想不出什么更恰当...
1 2 3 4 5 ... 20
收藏数 509,761
精华内容 203,904
关键字:

shared