精华内容
下载资源
问答
  • 由于类的非静态成员函数中有一个隐形的this指针,因此,类的成员函数的指针和一般函数的指针的表现形式不一样。 1、指向一般函数的指针函数指针的声明中就包括了函数的参数类型、顺序和返回值,只能把相匹配的函数...
  • 之前一直在讨论虚函数有关的知识,但不能忘记最基本的类成员函数。 可以看下面这篇和我之前写的一篇先复习一下虚函数,虚函数表,虚继承等等 ...然后存储区域明确一下: ...有一篇好文,提到了类的静态成员函数

    之前一直在讨论虚函数有关的知识,但不能忘记最基本的类成员函数。
    可以看下面这篇和我之前写的一篇先复习一下虚函数,虚函数表,虚继承等等
    https://www.cnblogs.com/jerry19880126/p/3616999.html

    然后存储区域明确一下:
    C++中,
    虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区;
    虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区;
    在这里插入图片描述

    只提到了虚函数和存放位置,那么类额普通成员函数呢?
    有一篇好文,提到了类的静态成员函数
    原文在这
    它的总结:类中的虚函数是动态生成的,由虚函数表的指向进行访问,不为类的对象分配内存,就没有虚函数表就无法访问。
    类中的普通函数静态生成,不为类的对象分配内存也可访问。

    这篇文章的意思是,所有类成员函数和非成员函数代码存放在代码区;
    插播一下文中的题外话:

    举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。看下面两种情况:

    如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。

    如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。

    这里的意思是:
    c++中一个类中无非有四种成员:静态数据成员和非静态数据成员,静态函数和非静态函数。

    1、非静态数据成员被放在每一个对象体内作为对象专有的数据成员。

    2、静态数据成员被提取出来放在程序的静态数据区内,为该类所有对象共享,因此只存在一份。

    3、静态和非静态成员函数最终都被提取出来放在程序的代码段中并为该类所有对象共享,因此每一个成员函数也只能存在一份代码实体。在c++中类的成员函数都是保存在静态存储区中的 ,那静态函数也是保存在静态存储区中的,他们都是在类中保存同一个惫份。

    因此,构成对象本身的只有数据,任何成员函数都不隶属于任何一个对象,非静态成员函数与对象的关系就是绑定,绑定的中介就是this指针。成员函数为该类所有对象共享,不仅是处于简化语言实现、节省存储的目的,而且是为了使同类对象有一致的行为。同类对象的行为虽然一致,但是操作不同的数据成员。

    综合上面几篇,他们的意思基本都是:他们统统放在静态存储区即只读数据段,但具体关系没有一个准确说明的,明早爬起来试验一下。。。

    展开全文
  • 类的默认成员函数

    2021-05-14 16:27:18
    类的6个默认成员函数类的6个默认成员函数1.构造函数2.析构函数拷贝构造函数 类的6个默认成员函数 如果一个类中什么成员都没有,简称为空...构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动

    类的默认成员函数

    如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数。
    在这里插入图片描述

    1.构造函数

    通过Date类来说明:

    对于Date类,可以通过SetDate公有的方法给对象设置内容,但是如果每次创建对象都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
    构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。

    #include<iostream>
    using namespace std;
    class Date
    {
    public:
    	void SetDate(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Display()
    	{
    		cout << _year << "-" << _month << "-" << _day << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    int main()
    {
    	Date d1;
    	d1.SetDate(2021, 1, 1);
    	d1.Display();
    
    	Date d2;
    	d2.SetDate(2021, 5, 1);
    	d2.Display();
    	return 0;
    }
    

    对于Date类,可以通过SetDate公有的方法给对象设置内容,但是如果每次创建对象都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
    构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。

    构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
    特征如下:

    1. 函数名与类名相同。
    2. 无返回值。
    3. 对象实例化时编译器自动调用对应的构造函数。
    4. 构造函数可以重载。
    #include<iostream>
    using namespace std;
    class Date
    {
    public:
    	Date()//无参构造函数
    	{
    		cout << "Date()" << endl;
    	}
    	Date(int year, int month, int day)//有参构造函数
    	{
    		cout << "Date(int year, int month, int day)" << endl;
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    int main()
    {
    	Date a;//当调用无参构造时后面不用加括号
    	Date b(2021, 5, 14);
    	return 0;
    }
    

    上述Date类的的有参和无参构造函数构成了重载,构造函数虽然可以重载,但是对于同一个定义的对象只能使用一个,否则有歧义。

    当然如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
    编译器生成的默认构造函数做了啥,他是有区别对待的:
    对于成员变量中的基本类型,他什么也不会做。
    对于成员变量中的自定义类型,他会去调用他会去调他的默认构造函数(不用参数就可以调用的:无参构造函数、全缺省构造函数、编译器生成的构造函数)
    如下:

    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	A()
    	{
    		cout << "A()"<<endl;
    	}
    private:
    	int a;
    };
    class Date
    {
    private:
    	int _year;
    	int _month;
    	int _day;
    	A a;
    };
    int main()
    {
    	Date a;
    	return 0;
    }
    

    在这里插入图片描述

    2.析构函数

    析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作,析构函数是特殊的成员函数。
    特征如下:

    1. 析构函数名是在类名前加上字符 ~。
    2. 无参数无返回值。
    3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
    4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	void create(int n)
    	{
    		cout << "create(int a)" << endl;
    		_a = (int*)malloc(sizeof(int)*n);
    	}
    	~A()
    	{
    		if (_a)
    		{
    			cout << "~A()" << endl;
    			free(_a);
    		}
    	}
    public:
    	int* _a;
    };
    int main()
    {
    	A a;
    	//a.create(1);
    	return 0;
    }
    

    在这里插入图片描述
    当解除a.create的屏蔽后
    在这里插入图片描述
    想Date类,没有要释放的的资源,所以不需要自己写析构函数,编译器自动生成的就够了。但是只要我们的类中动态开辟了空间如stack这种,一定要自己实现析构函数。
    构造函数和析构函数的最大特点就是会自动调用

    class A
    {
    public:
    	A(int a = 1)
    	{
    		cout <<this<< ":构造函数" << endl;
    	}
    	~A()
    	{
    		cout <<this<< ":析构函数" << endl;
    	}
    private:
    	int _a;
    };
    int main()
    {
    	A a1;
    	A a2;
    	cout << "&a1:" << &a1 << " &a2:" << &a2 << endl;
    }
    

    在这里插入图片描述
    从上面这代码和运行结果看出来什么?
    定义对象性时构造函数和析构函数的最大特点就是会自动调用,并且后定义的先析构,构造函数在定义时调用,析构函数在生命周期结束时调用。

    3.拷贝构造函数

    在创建对象时,可否创建一个与一个对象一某一样的新对象呢?
    比如:

    int a=10;
    int b=a;
    

    就像上述代码这样在创建时可以用同类型去初始化
    所以有了拷贝构造函数
    构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
    特征
    拷贝构造函数也是特殊的成员函数,其特征如下:

    1. 拷贝构造函数是构造函数的一个重载形式。
    2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
    3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。

    来一段代码演示:(暂时不考虑日期是否正确)

    #include<iostream>
    using namespace std;
    class Date
    {
    public:
    	Date(int year = 2021, int month = 5, int day = 14)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    	void print()
    	{
    		cout << _year << "-" << _month << "-" << _day << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    int main()
    {
    	Date d1(2021, 5, 15);
    	Date d2(d1);
    	Date d3 = d1;
    	d1.print();
    	d2.print();
    	d3.print();
    	return 0;
    }
    

    结果:
    在这里插入图片描述

    从这里看到

    	Date d2(d1);
    	Date d3 = d1;
    

    这两句代码效果一样
    看一下上述代码拷贝构造函数的结构

    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    

    定义规定参数就只能有一个,这没问题。
    但是为什么他的参数是引用类型的呢?如果就是普通的自定义类型呢
    思考一下
    是不是会有下面这种情况
    在这里插入图片描述
    所以这里我们不妨试试用引用类型,为什么用可以用引用呢?
    引用就是一个变量的别名,所以用引用时在上图中传参时就不会调用拷贝构造,这里就直接相当于把实参(因为引用定义的时候就是相当于那个数,只是名字不同)直接拿过来,而不会产生临时变量来存储实参
    在这里插入图片描述
    当然这里加const是为了安全考虑,前面讲引用的文章也讲过,引用传参时若不改变实参的值就尽量加const,不然有时候误操作或者不小心写错代码会改变实参的值。

    当然拷贝构造函数我们不写编译器也会自动生成,向上面这种日期类编译器自动生成的就够了,编译器生成的拷贝构造能完成值拷贝也称浅拷贝。
    但是当我们在写像stack这种类时就得自己实现了,举个栗子:

    class Stack
    {
    public:
    	Stack(int capacity=1)
    	{
    		_size = 0;
    		_capacity=capacity;
    		_a = (int*)malloc(sizeof(int)*_capacity);
    	}
    private:
    	int _size;
    	int _capacity;
    	int *_a;
    };
    int main()
    {
    	Stack s1;
    	Stack s2(s1);
    }
    

    在这里插入图片描述
    从调试中能不能看到什么问题
    发现s1和s2两个对象中的_a也值变一样了,那么当我们改变s2的的时候,s1是不是也会受到改变,这就会出问题了。
    所以像这种类型的类千万不能用编译器默认生成的拷贝构造函数,得自己写,自己实现深拷贝。
    总之,视情况而定。

    4.赋值运算符重载

    C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
    函数名字为:关键字operator后面接需要重载的运算符符号。
    函数原型:返回值类型 operator操作符(参数列表)
    注意:
    1.不能通过连接其他符号来创建新的操作符:比如operator@
    2.重载操作符必须有一个类类型或者枚举类型的操作数
    3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
    4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
    操作符有一个默认的形参this,限定为第一个形参
    5.(.* 、:: 、sizeof 、?: 、.) 注意以上5个运算符不能重载。

    当运算符重载在全局:

    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	private:
    	int _year;
    	int _month;
    	int _day;
    };
    // 这里会发现运算符重载成全局的就需要成员变量是共有的,那么问题来了,封装性如何保证?
    // 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
    bool operator==(const Date& d1, const Date& d2) 
    {
    	return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
    }
    void main()
    {
    	Date d1(2021, 5, 15);
    	Date d2(2021, 6, 16);
    	cout << (d1 == d2) << endl;
    }
    

    显然上面这段代码会有错误,因为在在类外是不能直接访问私有域的数据的,若是将私有域改为公有这样到时可以解决问题了,那么问题来了,封装性如何保证?
    所以,我们可以将运算符重载放在类里面。

    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	bool operator==(const Date& d1, const Date& d2)
    	{
    		return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
    	}
    	private:
    	int _year;
    	int _month;
    	int _day;
    };
    void main()
    {
    	Date d1(2021, 5, 15);
    	Date d2(2021, 6, 16);
    	cout << (d1 == d2) << endl;
    }
    

    但是这样编译器又会报错了
    在这里插入图片描述
    为什么?
    this指针,编译器会自动向类里面的函数添加一个隐含的this指针,前面也有讲过,就不在这里细讲了。
    在这里插入图片描述
    所以优化后会有三个参数,与函数调用的参数数量不符。
    this指针指向的就是这个类,所以在类里重载时只需要另一个类传过来就行了,用引用传参可以减少拷贝。所以改一下有:

    	bool operator==( Date& d2)
    	{
    		return (*this)._year == d2._year&& (*this)._month == d2._month&& (*this)._day == d2._day;
    	}
    

    *this就是当前类。
    运行结果
    在这里插入图片描述
    调用时注意参数位置就行了。
    赋值运算符主要有四点:

    1. 参数类型
    2. 返回值
    3. 检测是否自己给自己赋值
    4. 返回*this
    5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。

    若是遇到下面这种情况呢?
    当我们重载了=用来赋值

    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	void operator=(const Date& d2)
    	{
    		this->_year = d2._year;
    		this->_month = d2._month;
    		this->_day = d2._day;
    	}
    	void print()
    	{
    		cout << _year << "-" << _month << "-" << _day << endl;
    	}
    	private:
    	int _year;
    	int _month;
    	int _day;
    };
    void main()
    {
    	Date d1(2021, 5, 15);
    	Date d2(2021, 6, 16);
    	Date d3, d4;
    	d2 = d1;
    	d4 = d3 = d2;
    	d1.print();
    	d2.print();
    }
    

    d4 = d3 = d2;
    向上面这种连续赋值,我们就得改代码了,若是重载后的运算符是无返回值类型,那么上面这个语句就会出错,得返回一个和d3、d4一样类型的值。

    	Date operator=(const Date& d2)
    	{
    		this->_year = d2._year;
    		this->_month = d2._month;
    		this->_day = d2._day;
    		return d2;
    	}
    

    直接返回d2可以,那这里是不是就要调拷贝构造函数了,还是像前面常用的引用一样,直接传引用就不用拷贝构造了。改为

    	Date& operator=(const Date& d2)
    	{
    		this->_year = d2._year;
    		this->_month = d2._month;
    		this->_day = d2._day;
    		return *this;
    	}
    

    但是前面将引用的时候讲过,引用返回的时候要保证这个值的生命周期,这里可以成功返回码?
    在这里插入图片描述
    就算出来这个作用域销毁了this,但是Date&确已是d3的别名了,d3要销毁也得等程序结束了。

    赋值运算符重载(d2=d1这种)和拷贝构造一样,不写的话编译器也会自动生成,他会完成值拷贝,但是像stack这样的类就不能用默认生成的,跟默认生成的拷贝构造一样,他只会完成浅拷贝,会影响到他前面的对象,要用深拷贝就得自己去实现了。

    展开全文
  • 突发奇想给遍历成员函数传递一个函数成员指针,用来访问节点数据,并在内定义成员函数vist,作为默认访问函数,如下面的BFS遍历, template void BST::breadthFirst(void(*func)(BSTNode *) = &

    前几天相中并买下了一本《C++数据结构与算法》,的确是相中了,封面看起来B格就很高,今天把它拿出来抖抖灰,敲了书上P175的一段二叉查找树的代码。突发奇想给遍历成员函数传递一个函数成员指针,用来访问节点数据,并在类内定义成员函数vist,作为默认的访问函数,如下面的BFS遍历,

    template <typename T>
    void BST<T>::breadthFirst(void(*func)(BSTNode<T> *) = &BST<T>::vist)
    {
    	std::queue<BSTNode<T> *> que;
    	BSTNode<T> *p = root;
    	if (p == 0)
    		return;
    	que.push(p);
    	while (!que.empty())
    	{
    		p = que.front();
    		que.pop();	
    		if (p->left)
    			que.push(p->left);
    		if (p->right)
    			que.push(p->right);
    		func(p);
    	}
    }
    //BST::vist
    template <typename T>
    inline void BST<T>::vist(BSTNode<T> *node)
    {
    	cout << node->val << ' ';
    	return;
    }
    结果编译时报错:

    错误 1 error C2440: “默认参数”: 无法从“void (__thiscall BST<int>::* )(BSTNode<T> *)”转换为“void (__cdecl *)(BSTNode<T> *)”

    话说以前并没有见到过类似报错,在捣鼓了老半天之后,一拍脑门想到:thiscall 和cdecl,是不是少一个static关键字? 加上!果然就喜闻乐见的编译通过了!


    //定义不变,类内的声明加上static关键字
    static void vist(BSTNode<T> *);
    在网上找了一下,看到这个帖子

    http://bbs.csdn.net/topics/390246787

    1、类没有实例化前,只能用静态成员函数操作

    显然在传递成员函数指针时,并没有实例化的类,所以必须是static成员函数

    源代码:

    <p>
    </p><pre name="code" class="cpp">/* main.cpp */
    #include "genBST.h"
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    int main()
    {
    	BST<int> bst;
    	srand((unsigned int)time(NULL));
    	for (int i = 0; i < 20; ++i)
    	{
    		int temp = rand() % 100;
    		bst.insert(temp);
    	}
    	bst.inorder();
    	std::cout << endl;
    	bst.preorder();
    	cout << endl;
    	bst.postorder();
    	cout << endl;
    	bst.breadthFirst();
    	cout << endl;
    	int n;
    	//while (cin >> n)
    	//{
    	//	BSTNode<int> * p=bst.search(n);
    	//	if (p)
    	//		cout << p << ' ';
    	//}
    	return 0;
    }
    

    /* genBST.H */
    #include <iostream>
    #include <string>
    #include <queue>
    template<typename T>
    class BSTNode
    {
    public:
    	BSTNode(){
    		left = right = 0;
    	}
    	BSTNode(const T &v, BSTNode<T> *l = 0, BSTNode<T> *r = 0)
    	{
    		val = v;
    		left = l;
    		right = r;
    	}
    	T val;
    	BSTNode<T> *left, *right;
    };
    template <typename T>
    class BST
    {
    private:
    	BSTNode<T> * root;
    	void clear();
    	BSTNode<T> *search(BSTNode<T> *, const T &) const;
    	void preorder(BSTNode<T> *, void(*func)(BSTNode<T> *));
    	void inorder(BSTNode<T> *, void(*func)(BSTNode<T> *) );
    	void postorder(BSTNode<T> *, void(*func)(BSTNode<T> *));
    public:
    	BST()
    	{
    		root = 0;
    	}
    	~BST()
    	{
    		clear();
    	}
    
    	bool empty() const
    	{
    		return root == 0;
    	}
    	void preorder(void(*func)(BSTNode<T> *) = &BST<T>::vist)
    	{
    		preorder(root,func);
    	}
    	void inorder(void(*func)(BSTNode<T> *) = &BST<T>::vist)
    	{
    		inorder(root,func);
    	}
    	void postorder(void(*func)(BSTNode<T> *) = &BST<T>::vist)
    	{
    		postorder(root,func);
    	}
    	BSTNode<T> * search(const T &v) const
    	{
    		return search(root, v);
    	}
    	static void vist(BSTNode<T> *);
    	static void del(BSTNode<T> *);
    	void breadthFirst(void(*func)(BSTNode<T> *) = &BST<T>::vist);
    	void insert(const T &);
    };
    template <typename T>
    BSTNode<T> * BST<T>::search(BSTNode<T> *root, const T &v) const
    {
    	
    	BSTNode<T> *p = root;
    	while (p)
    	{
    		if (p->val == v)
    			break;
    		else if (p->val > v)
    			p = p->left;
    		else
    			p = p->right;
    	}
    	return p;
    }
    template <typename T>
    void BST<T>::breadthFirst(void(*func)(BSTNode<T> *) = &BST<T>::vist)
    {
    	std::queue<BSTNode<T> *> que;
    	BSTNode<T> *p = root;
    	if (p == 0)
    		return;
    	que.push(p);
    	while (!que.empty())
    	{
    		p = que.front();
    		que.pop();	
    		if (p->left)
    			que.push(p->left);
    		if (p->right)
    			que.push(p->right);
    		func(p);
    	}
    }
    template <typename T>
    void BST<T>::preorder(BSTNode<T> *root, void(*func)(BSTNode<T> *))
    {
    	BSTNode<T> *p = root;
    	if (p)
    	{
    		func(p);
    		if (p->left)
    			preorder(p->left,func);
    		if (p->right)
    			preorder(p->right,func);
    	}
    }
    template <typename T>
    void BST<T>::inorder(BSTNode<T> *root, void(*func)(BSTNode<T> *))
    {
    	BSTNode<T> * p = root;
    	if (p)
    	{
    		if (p->left)
    			inorder(p->left, func);
    		func(p);
    		if (p->right)
    			inorder(p->right, func);
    	}
    }
    template <typename T>
    void BST<T>::postorder(BSTNode<T> *root, void(*func)(BSTNode<T> *))
    {
    	BSTNode<T> *p = root;
    	if (p)
    	{
    		if (p->left)
    			postorder(p->left, func);
    		if(p->right)
    			postorder(p->right, func);
    		func(p);
    	}
    }
    template <typename T>
    void BST<T>::insert(const T &v)
    {
    	if (!root)
    	{
    		root = new BSTNode<T>(v);
    		return;
    	}
    	BSTNode<T> *p = root;
    	while (1)
    	{
    		if (p->val == v)
    			return;
    		else if (p->val > v)
    		{
    			if (!p->left)
    			{
    				p->left = new BSTNode<T>(v);
    				return;
    			}
    			p = p->left;
    		}
    		else
    		{
    			if (!p->right)
    			{
    				p->right = new BSTNode<T>(v);
    				return;
    			}
    			p = p->right;
    		}
    	}
    }
    template <typename T>
    inline void BST<T>::vist(BSTNode<T> *node)
    {
    	cout << node->val << ' ';
    	return;
    }
    template <typename T>
    void BST<T>::del(BSTNode<T> *root)
    {
    	delete root;
    	root = 0;
    }
    template <typename T>
    void BST<T>::clear()
    {
    	breadthFirst(&BST<T>::del);
    	root = 0;
    }
    </pre>



    展开全文
  • (1)下面这个错误意思我在我非const成员中调用了const成员函数,这会报错!!!! In file included from circularListWithHeader.cpp:1:0: circularListWithHeader.h: In instantiation of ‘T& ...
    关于类的const成员的一些小细节要注意:
    (1)下面这个错误的意思是我在我的非const成员中调用了const成员函数,这是会报错的!!!!
    In file included from circularListWithHeader.cpp:1:0:
    circularListWithHeader.h: In instantiation of ‘T& circularListWithHeader<T>::get(int) const [with T = int]:
    circularListWithHeader.cpp:10:1:   required from here
    circularListWithHeader.h:45:15: error: passing ‘const circularListWithHeader<int>’ as ‘this’ argument discards qualifiers [-fpermissive]
         checkIndex(theIndex);
                   ^
    circularListWithHeader.h:120:27: note:   in call to ‘void circularListWithHeader<T>::checkIndex(int) [with T = int]template<typename T> void circularListWithHeader<T>::checkIndex(int theIndex){
    为什么会报错呢?
    首先所有的非const成员函数中都有一个默认的形参为 class *const this(这样表示是错误的,这里只是想让大家更容易明白这个this参数)。this参数表示的是指向本对象的一个指针。然后它是一个const的,也就是说this的值是不能改变的,它指向的对象里边的内容是可以被改变的(这里简单点说就是,我的this指向的对象是不能改变的,也就是说this表示的只能是其对象本身;但是这个对象里边的内容是可以改变的,也就是它的成员是可以被改变的)
    所以这个 class *const this就很好的表现了这一点。然后如果是常量对象,那么它传入的this就是const class * const this。所以这个时候,
    我的常量对象只能去调用const成员函数。之所以是这样是因为 const class * const 对象只能被const class * const接受,是不可以被
    class * const 对象接受的!!!
    所以常量对象只能调用常量成员函数。
    接下来分析下非常量成员函数与常量成员函数之间的调用关系:
    	因为在成员函数调用另一个成员函数的时候,this是隐式传递的。所以一个非常量成员函数(class *const this),调用常量成员函数的时候
    传递this过程是:
    			class *const this 要变成 const class * const this了,这种改变是可以的。
    所以非常量成员函数是可以调用常量成员函数的。
    	但是反过来,如果常量成员函数调用非常量成员函数,那么进行的this转换就是
    			const class * const this 转成 class * const this;这个转换就是错误的了
    因为上面的转换是错误的,所以常量成员函数是不能调用非常量成员函数的。
    小结下:
    	(1)常量类对象只能调用常量成员函数。非常量类对象既可以调用常量成员函数,也可以调用非常量成员函数
    	(2)在类内部,常量成员函数只能调用常量成员函数;非常量成员函数既可以调用常量成员函数也可以调用非常量成员函数。
    
    展开全文
  • C++之类的默认成员函数 目录类的6个默认成员函数构造函数析构函数拷贝构造函数赋值操作符重载 类的6个默认成员函数 如果一个类中什么成员都没有,简称...构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫
  • 1、C++中class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。从计算机角度,程序依然由数据段(栈区内存)和代码段(代码区内存)构成。 那么C++编译器如何管理...
  • 类这种数据类型一个包含成员变量和成员函数的一个集合。下面是student类的定义。 [例1] 类可以包含成员变量和成员函数: class student { char name[20]; //姓名 int id_num; //学号 int age; //年龄 char sex; //...
  • 在MFC的很多程序中,常常需要在回调函数中调用MFC类的类成员变量、类成员函数,亦或者对话框控件的句柄。...而下面这种方法便无需把回调函数设为类的静态成员,也能够顺利的访问类的成员和对话框控件。 一、先在对
  • C++类的特殊成员函数

    2018-04-01 16:29:34
    特殊成员函数的难点在于它们何时被调用,以及可能产生的一些问题。 构造函数 每个都有自己的构造函数,即使一个构造函数都没定义,编译器也会自动生成默认构造函数。构造函数下面有细分的种类: 构造函数 每...
  • 2. 因为不属于,在实例化之前就已经存在,所以静态成员函数是不能调用普通成员函数和成员变量。 3. 静态成员函数没有this指针,因而不能用const声明。 不属于对象、没有this指针,那怎么静态成员函数怎么...
  • 函数指针,顾名思义就是函数的指针,而指针其实就是地址,那么函数指针就是存储函数的地址,可是实际大部分时间里,我们写程序时根本不会去考虑函数地址在哪里。我们只需要知道函数原型和函数声明就可以。但是想象...
  • C++类的默认成员函数

    2018-05-24 12:47:57
    C++有六个默认的成员函数,分别构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、&amp;运算符重载函数、const &amp;运算符重载函数。下面分别说明:定义一个class CTestClass{};。1、构造函数...
  • 特殊成员函数

    2020-05-07 22:17:05
      C++自动提供了下面这些成员函数: • 默认构造函数,如果没有定义构造函数; • 默认析构函数,如果没有定义; • 复制构造函数,如果没有定义;   新建一个对象并将其初始化为同类现有对象时,复制构造...
  • 复习C++ Primer的时候,看到了关于C++类的内联成员函数的放置,应该放在头文件中。那么这到底为什么呢?仅仅一种代码规范问题还是...而我们类的成员函数的实现都放在相应的.cpp文件中的,而在.h文件中声明。这样
  • 下面是代码//main.cpp #include"CTime.h" #include"CDate.h" #include void main() { CTime t1(10, 13, 56); CDate d1(2004, 15, 12); CTime *p1 = &t1; t1.ShowValue(&d1); system("paus
  • 比如:抽象出来一个叫person,基类(//基类下层关系子类/派生,子类派生一个东西),基类就是最顶层那个东东,再下面就是它孩子也就是派生出来的类,派生或者子类,比如学生student /**********...
  • 类的六个默认成员函数 六个默认成员函数 如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不的,任何一个类 在我们不写的情 况下,都会自动生成下面6个默认成员函数。 class Date {}; 1.构造...
  • 它们提供类级别的访问控制,但是类的成员函数可以访问同一类的所有对象的所有私有成员,例如下面的C++示例: #include "stdafx.h" #include class test { protected: private: int ivalue; public: test()
  • 类和对象2 ( 类的成员函数、构造函数、析构函数、拷贝构造函数、赋值运算符重载、const成员) 类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?不是的。任何一个类在我们不写的...
  • C++一个类的成员函数作为另一个类的友元函数

    千次阅读 多人点赞 2019-04-27 18:17:07
    先上一段代码。定义了两个,都有私有变量num。分别用全局函数、友元函数计算两者和。... // 此行可不加,这里加此行因为下面举例子有用 class A { public: A(int n = 0): num(n) {} // 构造函数 ...
  • 本文详细给大家介绍了关于python用dir函数查看中所有成员函数的相关内容,下面话不多说了,来一起看看详细的介绍吧。 可以使用下面的代码: # File: builtin-dir-example-2.py class A: def a(self): pass ...
  • 类友员函数访问类的私有成员例子: 我们知道类的私有成员只能被该类的成员函数和该类的friend函数访问。下面是举个关于类的友员函数反问类的私有成员简单的例子。#include using namespace std;class c{public: ...
  • 类的实例调用成员函数的原理 其实不管通过对象实例或指针实例调用,其实底层调用的过程都一样的,都把当前对象的指针作为一个参数传递给被调用的成员函数。通过下面的相关实例代码进行检验: 实验的C++代码 ...
  • > 错误 4 error C2511: “void Student::ShowData(Score &)”:“Student”中没有找到重载的成员函数 c:\users\xxxx\documents\visual studio 2013\projects\实验三\实验三\student.cpp 12 1 实验三 > > 错误 5 ...
  • 一:目的 1、在MFC的很多程序中,常常需要在回调函数中调用MFC类的类成员变量、类成员函数,亦或者对话框控件的句柄。...而下面这种方法便无需把回调函数设为类的静态成员,也能够顺利的访问类的成员和对话框控件。...
  • 7.7. 类的成员函数 成员函数的定义与普通函数的定义类似。和任何函数一样,成员函数也包含下面四个部分: 函数返回类型。 函数名。 用逗号隔开的形参表(也可能空的)。 包含在一对花括号里面的...
  • 如果一个中什么成员都没有,则就是空。...构造函数是一个特殊的成员函数,名字与类名相同,创建类型对象时由编译器自动调用,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。 ...
  • 指向基类的虚成员函数的指针赋值为基类的虚成员函数地址,如果派生类覆写了基类的对应虚函数,那么当用派生类的对象来调用该虚成员函数指针时,调用的是派生类虚函数!见下面的实例。 #include <iostream> ...
  • 通过C++11的bind和function实现类的成员函数作为回调函数下面是一简单例子:#include "stdafx.h" #include &lt;functional&gt; using namespace std; using namespace std::placeholders; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,080
精华内容 1,632
关键字:

下面函数是类的成员函数的是