精华内容
下载资源
问答
  • 结构体函数指针初始化

    千次阅读 2012-04-18 14:23:45
    最近一段时间以来,都在想着怎样将程序分层进行模块化,减小程序...函数指针进行初始化工作方能调用,就好比windows里面的窗口调用一样,在使用之前,先要窗口类进行注册一个道理。但是又想怎样可 以将函数指针进

              最近一段时间以来,都在想着怎样将程序分层进行模块化,减小程序之间的耦合性。在自学C++一段时间以后,于是有模仿C++的想法,

    将相同或者类似的函数都放在同一模块内,能后通过函数指针的方式调用起模块内部的函数。但是在定义函数指针之后,需要对起模块内部

    函数指针进行初始化工作方能调用,就好比windows里面的窗口调用一样,在使用之前,先要对窗口类进行注册一个道理。但是又想怎样可

    以将函数指针进行常量化初始化,从而保证能像普通函数那样一样调用。如果使用这种方法,必须手工去初始化函数指针,这样会容易产生

    人为错误。

            在一次看网页的时候,偶然之间在黑金开发版搜索到的一个例子程序,看到了以下类似的函数指针初始化工作。

    void module_initial(void);
    
    typedef struct {
        void (* initial) (void);
    }MODULE_T;
    
    MODULE_T module = {
        .initial = module_initial
    };
    
    void module_initial(void)
    {
    //............
    }

    可以将此模块放在单独一个模块内。这样在其他程序就可以使用module.initial()来对 module_initial来进行调用了。同理,

    模块内的其他函数也可以通过函数指针的方式来实现调用,这样就提高了程序的内聚性,但是此程序在keil中编译的时候,

    需要在C/C++编译选项里添加--C99才能编译通过,否则会报错。使用这种方法将会消耗一定的内存资源。调用module.intial()的反汇编为

    ;;;65         module.initial();
    00003c  4914              LDR      r1,|L1.144|
    00003e  6808              LDR      r0,[r1,#0]  ; module
    000040  4780              BLX      r0


    其实质上是编译器帮程序员完成了初始化工作,汇编中所指向的函数地址存储在RAM中。

    但是有没有更好的方式可以节省RAM的方式来完成直接调用,不知道哪位大虾有没有更好的方法,还请各位指教。


    展开全文
  • 1、当创建类的新对象时,初始化指针,并将引用计数设置为1 2、当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针相应的引用计数(加1) 3、使用赋值操作符一个对象进行赋值时,处理复杂...

    目录

    计数原理

    智能指针的简单实现

    1.智能指针是什么

    2.实现

    3、改进

    智能指针类的改进一

    智能指针改进二

    为什么多线程读写 shared_ptr 要加锁?


    计数原理

    智能指针将一个计数器类指向的对象关联,引用计数跟踪共有多少个类对象(shared_ptr对象?)共享同一指针。它的具体做法如下:

    1、当创建类的新对象时,初始化指针,并将引用计数设置为1

    2、当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针相应的引用计数(加1)

    3、使用赋值操作符对一个对象进行赋值时,处理复杂一点:

    (赋值判断要先判断 是否是自己给自己赋值)

    先使左操作数的指针的引用计数减1(为何减1:因为指针已经指向别的地方),如果减1后引用计数为0,则释放指针所指对象内存。

    然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象)。

    析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete对象。

     

    智能指针的简单实现

     

    1.智能指针是什么

    简单来说,智能指针是一个类,它对普通指针进行封装,使智能指针类对象具有普通指针类型一样的操作。具体而言,复制对象时,副本和原对象都指向同一存储区域,如果通过一个副本改变其所指的值,则通过另一对象访问的值也会改变.所不同的是,智能指针能够对内存进行进行自动管理,避免出现悬垂指针等情况。

    2.实现

    了解了引用计数,我们可以使用它来写我们的智能指针类了。智能指针的实现策略有两种:辅助类与句柄类。这里介绍辅助类的实现方法。

    4.1.基础对象类

    首先,我们来定义一个基础对象类(基础对象类:int、string、自定义的Ponit类等)Point类,为了方便后面我们验证智能指针是否有效,我们为Point类创建如下接口:

    class Point                                      
    {
    
    public:
    
        Point(int xVal = 0, int yVal = 0) :x(xVal), y(yVal) { }
    
        int getX() const { return x; }
        int getY() const { return y; }
        void setX(int xVal) { x = xVal; }
        void setY(int yVal) { y = yVal; }
    
    private:
    
        int x, y;
    
    };

     

    4.2.辅助类(指针和计数管理)

    在创建智能指针类之前,我们先创建一个辅助类。这个类的所有成员皆为私有类型,因为它不被普通用户所使用。为了只为智能指针使用,还需要把智能指针类声明为辅助类的友元。这个辅助类含有两个数据成员:计数count与基础对象指针。也即辅助类用以封装使用计数与基础对象指针。

    辅助类:就是 把 int count;  和 Point *p; 封装成一个类,用于管理指针和计数)   

    class U_Ptr                                  
    {
    
    private:
    
        friend class SmartPtr;      
        U_Ptr(Point *ptr) :p(ptr), count(1) { }
        ~U_Ptr() { delete p; }
    
        int count;  
        Point *p;                                                      
    };

     

    4.3.为基础对象类实现智能指针类

    引用计数是实现智能指针的一种通用方法。智能指针将一个计数器与类指向的对象相关联,引用计数跟踪共有多少个类对象共享同一指针。它的具体做法如下:

    当创建类的新对象时,初始化指针,并将引用计数设置为1

    当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针相应的引用计数(加1)

    使用赋值操作符对一个对象进行赋值时,处理复杂一点:先使左操作数的指针的引用计数减1(为何减1:因为指针已经指向别的地方),如果减1后引用计数为0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象)。

    析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete对象。

    做好前面的准备后,我们可以来为基础对象类Point书写一个智能指针类了。根据引用计数实现关键点,我们可以写出我们的智能指针类如下:

    class SmartPtr
    {
    public:
    
        SmartPtr(Point *ptr) : rp(new U_Ptr(ptr)) { }
    
        SmartPtr(const SmartPtr &sp) : rp(sp.rp)
        {
            ++rp->count;
        }
    
        SmartPtr &operator=(const SmartPtr &rhs)
        {
    
            ++rhs.rp->count;
            if (--rp->count == 0)
                delete rp;
    
            rp = rhs.rp;
            return *this;
        }
    
    
        ~SmartPtr()
        {
    
            if (--rp->count == 0)
                delete rp;
            else
                cout << "还有" << rp->count << "个指针指向基础对象" << endl;
        }
    
    private:
        U_Ptr *rp;
    };

    为什么U_Ptr  *rp,而不是 static Uptr rp 或者 

    参考:

    // 此处有个重点的知识点

    在没有封装成U_ptr类时,一般这样定义

     int* m_pCount; 
     Point *p; 

    为什么不用static int m_pCount、int m_pCount:


    1、若使用 static int m_pCount,则在一个new资源块(实例)下可以实现计数功能
    若出现多个new资源块(实例)时,将无法实现多个资源块儿的正确释放,因为static属于类共享而不是资源块儿(对象)绑定。(多实例共享static)


     2、若使用int m_pCount则与对象个数绑定,无法实现一个资源块儿下的多个指针对象m_pCount相同
    则无法实现--m_pCount功能计数


    3、为当前案例方式*m_pCount ,可以实现多个new资源块儿时,同步new m_pCount;
    即可实现资源块儿与独立m_pCount资源的绑定,方便管理计数,此情况下,在拷贝的时候,将多个拷贝
    关系的变量直接使用m_pCount地址传递,达到m_pCount资源在同一个资源块儿下多个对象共享的机制
        int* m_pCount; // 资源计数器,为0时可以被释放,避免重复释放问题

    连接:https://zhuanlan.zhihu.com/p/161629362

    4.4.智能指针类的使用与测试

    至此,我们的智能指针类就完成了,我们可以来看看如何使用

    int main()
    {
    
        //定义一个基础对象类指针
        Point *pa = new Point(10, 20);
    
        //定义三个智能指针类对象,对象都指向基础类对象pa
        //使用花括号控制三个指针指针的生命期,观察计数的变化
    
        {
            SmartPtr sptr1(pa); //此时计数count=1
            {
                SmartPtr sptr2(sptr1); //调用复制构造函数,此时计数为count=2
    
                {
                    SmartPtr sptr3 = sptr1; //调用赋值操作符,此时计数为conut=3
                }
                //此时count=2
    
            }
            //此时count=1;
    
        }
    
        //此时count=0;pa对象被delete掉
        cout << pa->getX () << endl;
        system("pause");
    
        return 0;
    }

    来看看运行结果咯:

    如期,在离开大括号后,共享基础对象的指针从3->2->1->0变换,最后计数为0时,pa对象被delete,此时使用getX()已经获取不到原来的值。有兴趣一起交流学习c/c++的小伙伴可以加群:941636044,里面有大神会给予解答,也会有许多的资源可以供大家学习分享,欢迎大家前来一起学习进步!

    3、改进

    智能指针类的改进一

    虽然我们的SmartPtr类称为智能指针,但它目前并不能像真正的指针那样有->、*等操作符,为了使它看起来更像一个指针,我们来为它重载这些操作符。代码如下所示:

    {
    
    public:
    
        SmartPtr(Point *ptr) :rp(new U_Ptr(ptr)) { }    
        SmartPtr(const SmartPtr &sp) :rp(sp.rp) { ++rp->count; }
        SmartPtr& operator=(const SmartPtr& rhs) {    
    
            ++rhs.rp->count;    
            if (--rp->count == 0)    
                delete rp;
    
            rp = rhs.rp;
            return *this;
        }
    
        ~SmartPtr() {      
    
            if (--rp->count == 0)  
                delete rp;
            else
            cout << "还有" << rp->count << "个指针指向基础对象" << endl;
    
        }
    
    
        Point & operator *()        //重载*操作符  
        {
            return *(rp->p);
        }
    
        Point* operator ->()       //重载->操作符  
        {
            return rp->p;
        }
    
    private:
    
        U_Ptr *rp;  
    
    };

    然后我们可以像指针般使用智能指针类

      

      Point *pa = new Point(10, 20);
    
        SmartPtr sptr1(pa);
    
        //像指针般使用
    
        cout<<sptr1->getX();

    智能指针改进二

    目前这个智能指针智能用于管理Point类的基础对象,如果此时定义了个矩阵的基础对象类,那不是还得重新写一个属于矩阵类的智能指针类吗?但是矩阵类的智能指针类设计思想和Point类一样啊,就不能借用吗?答案当然是能,那就是使用模板技术。为了使我们的智能指针适用于更多的基础对象类,我们有必要把智能指针类通过模板来实现。这里贴上上面的智能指针类的模板版:

    //模板类作为友元时要先有声明
    template <typename T>
    class SmartPtr;
    
    
    template <typename T>
    class U_Ptr   //辅助类
    {
    
    private :
    
        //该类成员访问权限全部为private,因为不想让用户直接使用该类
        friend class SmartPtr<T>;
    
        //定义智能指针类为友元,因为智能指针类需要直接操纵辅助类
        //构造函数的参数为基础对象的指针
        U_Ptr(T *ptr) : p(ptr), count(1) { }
    
        //析构函数
        ~U_Ptr()
        {
            delete p;
        }
    
        //引用计数
        int count;
    
        //基础对象指针
        T *p;
    };
    
    
    template <typename T>
    class SmartPtr  //智能指针类
    
    {
    
    public :
    
        SmartPtr(T *ptr) : rp(new U_Ptr<T>(ptr)) { }    //构造函数
    
        SmartPtr(const SmartPtr<T> &sp) : rp(sp.rp)
        {
            ++rp->count;
        }
    
        //复制构造函数
        SmartPtr &operator=(const SmartPtr<T> &rhs)
        {
    
            //重载赋值操作符
            ++rhs.rp->count;
            //首先将右操作数引用计数加1,
    
            if (--rp->count == 0)   //然后将引用计数减1,可以应对自赋值
    
                delete rp;
    
            rp = rhs.rp;
    
            return *this;
        }
    
    
    
        T &operator *()     //重载*操作符
        {
            return *(rp->p);
        }
    
        T *operator ->()    //重载->操作符
        {
            return rp->p;
    
        }
    
        ~SmartPtr()
        {
            //析构函数
            if (--rp->count == 0)   //当引用计数减为0时,删除辅助类对象指针,从而删除基础对象
    
                delete rp;
            else
                cout << "还有" << rp->count << "个指针指向基础对象" << endl;
        }
    
    private :
    
        U_Ptr<T> *rp;
        //辅助类对象指针
    };

    好啦,现在我们能够使用这个智能指针类对象来共享其他类型的基础对象啦,比如int:

    int main()
    {
        int *i = new int(2);
        {
    
            SmartPtr<int> ptr1(i);
            {
                SmartPtr<int> ptr2(ptr1);
                {
    
                    SmartPtr<int> ptr3 = ptr2;
    
                    cout << *ptr1 << endl;
    
                    *ptr1 = 20;
    
                    cout << *ptr2 << endl;
                }
    
            }
        }
    
        system("pause");
    
        return 0;
    }

    运行结果如期所愿,SmartPtr类管理起int类型来了:


    原文:链接:https://www.jianshu.com/p/e32037c905ac

    值得一看的文章:https://blog.csdn.net/zhangruijerry/article/details/100927531

    为什么多线程读写 shared_ptr 要加锁?

    http://www.cppblog.com/Solstice/archive/2016/04/01/197597.html

    因为 shared_ptr 有两个数据成员,读写操作不能原子化。

    展开全文
  • C/C++野指针

    2021-01-16 17:15:51
    C/C++野指针指针: 野指针不同于空指针,空指针是指一个指针...1.指针变量的值未被初始化: 声明一个指针的时候,没有显示的进行初始化,那么该指针所指向的地址空间是乱指一气的。如果指针声明在全局数据区,那

    C/C++野指针

    野指针:
    野指针不同于空指针,空指针是指一个指针的值为null,而野指针的值并不为null,野指针会指向一段实际的内存,只是它指向哪里我们并不知情,或者是它所指向的内存空间已经被释放,所以在实际使用的过程中,我们并不能通过指针判空去识别一个指针是否为野指针。避免野指针只能靠我们自己养成良好的编程习惯,下面说说哪些情况下会产生野指针,以及怎样避免。

    1.指针变量的值未被初始化: 声明一个指针的时候,没有显示的对其进行初始化,那么该指针所指向的地址空间是乱指一气的。如果指针声明在全局数据区,那么未初始化的指针缺省为空,如果指针声明在栈区,那么该指针会随意指向一个地址空间。所以良好的编程习惯就是在声明指针的时候就对其进行初始化,如果暂时不知道该初始化成什么值,就先把指针置空。

    void func()
    {
    int *ptr; // 野指针
    if (ptr != null) {
    ……do_somthing
    }
    }

    2.指针所指向的地址空间已经被free或delete:在堆上malloc或者new出来的地址空间,如果已经free或delete,那么此时堆上的内存已经被释放,但是指向该内存的指针如果没有人为的修改过,那么指针还会继续指向这段堆上已经被释放的内存,这时还通过该指针去访问堆上的内存,就会造成不可预知的结果,给程序带来隐患,所以良好的编程习惯是:内存被free或delete后,指向该内存的指针马上置空。

    void func()
    {
    int *ptr = new int[5];
    delete []ptr;
    // 执行完delete后,ptr野指针
    }

    3.指针操作超越了作用域:

    void func()
    {
    int *ptr = null;
    {
    int a = 10;
    ptr = &a;
    } // a的作用域到此结束

    int b = *ptr; // ptr指向a,a已经被回收,ptr野指针
    }

    展开全文
  • 在这之前不清楚引用,和指针怎样的关系,但今天一...然后在主函数中对初始化函数进行调用: InitSqList(&L);在调用这个函数的时候我们使用的是引用。 然后我们定义这个函数是的时候使用的是: int InitSqList(Sq

    在这之前不清楚引用,和指针有怎样的关系,但今天一个例子让我们知道了指针和引用在程序中怎样运用

    在这个地方我们是初始化初始化一个顺序表,

    我们在主函数中是定义一个结构体的变量:

    SqList L;

    然后在主函数中对初始化函数进行调用:

    InitSqList(&L);在调用这个函数的时候我们使用的是引用。

    然后我们定义这个函数是的时候使用的是:

    int InitSqList(SqList *L)//初始化一个顺序表
                                       //在这个地方函数的参数是表的一个指针,也就是我们初始化
                                     //在之后用到的表就是这一个表
    {
    L->elem=(ElemType *)malloc((LIST_INIT_SIZE)*sizeof(ElemType));//分配内存空间
    if(!L->elem)exit(OVERFLOW);//储存分配失败
    L->length=0;//初始化的时候元素中是什么都没有放的
    L->listsize=LIST_INIT_SIZE;//顺序表中存放的元素
    cout<<"\t\t顺初始化成功\n";
    return OK;
    }




    展开全文
  • 征服指针(C)

    2018-01-20 13:00:44
    5.2.7 对指针的恐惧 236 5.2.8 说到底,指针究竟是什么 237 第6章 其他——拾遗 239 6.1 陷阱 239 6.1.1 关于strncpy() 239 6.1.2 如果在早期的C中使用float类型的参数 240 6.1.3 printf()和scanf() 242 ...
  • 怎样使用类和对象

    2019-12-19 13:34:23
    利用构造函数类对象进行初始化 1、对象的初始化 2、构造函数实现数据成员的初始化 析构函数进行清理工作 调用构造函数和析构函数的顺序 对象数组 对象指针 公用数据保护 对象的动态建立和释放 对象的赋值...
  • c语言定义结构体可以直接声明结构体变量的,如下 struct Student{ ...我们在main函数中怎样对这个结构体进行初始化呢? 可以这样 int main() { xiaoming.age = 15; xiaoming.name = (...
  • C语言中我们可以使用const和define关键字声明常量,所谓常量就是指值不能修改的量。如下面的例子所示: int const a; const int a; 这两条语句都把a声明为一个常量...法1:在声明时进行初始化,如下所示...
  • c语言和java语言怎样填充数组元素

    千次阅读 2016-09-29 14:49:51
    memset为内存填充函数,可以用它一片内存空间进行初始化, 它可以一字节一字节地把整个数组设置为一个指定的值 其原型为 void *memset(void *s, int v, size_t n); 这里s可以是数组名,也可以是指向某一...
  • 目录目录 1const和引用的疑惑 什么是符号表符号表存储在程序中...当使用字面量变量进行初始化或赋值时 深入理解重载规则 4C方式编译的疑惑 深入理解extern C1、const和引用的疑惑关于const和引用的疑难问题,在前面的
  • 9.1.3 结构体变量的初始化和引用 9.2 使用结构体数组 9.2.1 定义结构体数组 9.2.2 结构体数组的应用举例 9.3 结构体指针 9.3.1 指向结构体变量的指针 9.3.2 指向结构体数组的指针 9.3.3 用结构体变量和结构体变量的...
  • A:我们知道,在存储结构体或联合(struct / union)这样的复合型变量时,计算机在内存空间中开辟一段连续的位置,按照成员变量定义的自然顺序进行初始化。但是往往结构体中的不同成员变量类型各异,存储起来其空间...
  • 什么是回调函数? 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针做为参数传递给另一个函数,当这个指针被用来...3) 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数事件进行处理。
  •  4.6 为什么不能void *指针进行算术操作?  4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思?  作为函数参数的指针  4.8 我有个函数,它...
  • A:我们知道,在存储结构体或联合(struct / union)这样的复合型变量时,计算机在内存空间中开辟一段连续的位置,按照成员变量定义的自然顺序进行初始化。但是往往结构体中的不同成员变量类型各异,存储起来其空间...
  • 4.6 为什么不能void *指针进行算术操作? 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 作为函数参数的指针 4.8 我有个函数,它应该接受并...
  • 你必须知道的495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    4.6 为什么不能void*指针进行算术操作? 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unalignedaccess”(未对齐的访问)的信息。这是什么意思? 作为函数参数的指针 4.8 我有个函数,它应该...
  • 4.6 为什么不能void *指针进行算术操作? 76 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 76 作为函数参数的指针 76 4.8 我有个函数,...
  • 4.6 为什么不能void *指针进行算术操作? 76 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 76 作为函数参数的指针 76 4.8 我有个函数,...
  • 4.6 为什么不能void *指针进行算术操作? 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 作为函数参数的指针 4.8 我有个函数,它应该接受并...
  • 4.6 为什么不能void*指针进行算术操作?  4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unalignedaccess”(未对齐的访问)的信息。这是什么意思? 作为函数参数的指针  4.8 我有个函数,它应该...
  •  4.6 为什么不能void*指针进行算术操作? 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unalignedaccess”(未对齐的访问)的信息。这是什么意思? 作为函数参数的指针 4.8 我有个函数,它应该...
  • 11.5 结构体数组11.5.1 定义结构体数组11.5.2 结构体数组的初始化11.5.3 结构体数组应用举例 11.6 指向结构体类型数据的指针11.6.1 指向结构体变量的指针11.6.2 指向结构体数组的指针11.6.3 用结构体变量和...
  • c++中,在使用一些基于范围的模板函数时,常常需要我们传入一个可调用对象,以指明我们需要范围中的每个元素进行怎样的处理。在thread的初始化中也需要传入可调用对象来作为线程的入口函数。可调用对象也可以作为...
  • 4.6 为什么不能void *指针进行算术操作? 47 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 47 作为函数参数的指针 47 4.8 我有个...
  • 《你必须知道的495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    4.6 为什么不能void *指针进行算术操作? 47 4.7 我有些解析外部结构的代码,但是它却崩溃了,显示出了“unaligned access”(未对齐的访问)的信息。这是什么意思? 47 作为函数参数的指针 47 4.8 我有个...
  • o 2.14 我总算弄清除函数指针的声明方法了, 但怎样才能初始化呢? * 3. 结构、联合和枚举 o 3.1 声明 struct x1 { ...}; 和 typedef struct { ...} x2; 有什么不同? o 3.2 为什么 struct x { ...}; x ...

空空如也

空空如也

1 2 3 4 5 6
收藏数 114
精华内容 45
关键字:

怎样对指针进行初始化