• 类成员指针
class Test
{
public:
	void output()
	{
		cout << "output not static" << endl;
	}
	static void staoutput()  //本质属于类
	{
		cout << "static function" << endl;
	}
private:
        static int m;
        int n;
};
int main()
{
        void(*ptr)() = &Test::staoutput;
        //只能使用这种不带作用域的指针接受静态成员函数
	ptr();                  //可以直接调用而不需要对象
        void(Test::*ptr2)() = &Test::output;
        Test t;
        (t.*ptr2)()               //使用对象调用output函数
        int* p = &Test::m;        //定义指向静态变量的指针
        *p = 100;                 //只有一份,直接修改
        int Test::*q = &Test::n;  //定义指向类成员的指针
        t.*q = 100                //通过对象修改
}
  • inline

作用:减少函数调用到来的花销,但inline只是对编译器的一种建议
在函数声明或定义中函数返回类型前加上关键字inline就可以把函数指定为内联函数。关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声明前不起任何作用。
inline关键字是一种提高效率的方法,但是加大了编译后的文件

  • mutable
      class Test
      {
      public:
	      void output() const
	      {
		      cout << "output not static" << endl;
		      m++;
	      }
      private:
	      mutable int m;  //使m可以在const成员函数中被修改
      };

注意:mutable不可以修饰常成员变量和静态成员变量

  • 两个类互为友元
class Test2;    //提前声明,因为Test2中函数参数要使用类Test
class Test
{
	friend class Test2;               //友元只是一种声明关系
public:
	void output(const Test2& test);   //只有声明,因为Test2类还没有没定义,无法调用Test2类中的成员
private:
	string name;
};

class Test2                             //Test2被实现了,这意味这,Test2中的成员可以被调用了
{
	friend class Test;
public:
	Test2() {}
	void output(const Test& test)
	{
		cout << test.name << endl;
	}

private:
	string name;
};
void Test::output(const Test2& test)   //实现Test类中使用Test2中成员的函数
{
	cout << test.name << endl;
}

当然了分文件写的话,就没有怎么多麻烦的事情了
友元的真正作用是实现一些必要的运算符重载,而不是处理类之间的关系

  • 重载运算符

输入输出流运算符的重载只能作为友元函数,因为两个参数是必要的

class Test
{
	friend ostream& operator<<(ostream& os, const Test& test);
	friend istream& operator>>(istream& in, Test& test);
        friend void operator++(Test& test);
public:
	Test operator+(const Test& test)   //在保证传入参数和*this本身不变的情况下相加
	{
		Test t;
		t.count = test.count + this->count;
		return t;
	}
private:
	unsigned count = 0;
};

ostream& operator<<(ostream& os, const Test& test)
{
	os << test.count;
        return os;
}
//返回 ostream& 是为了实现cout<<test<<endl的类似功能
istream& operator>>(istream& in, Test& test)
{
	in >> test.count;
        return in;
}
//重载一元运算符时,可以在类内重载也可以作为友元重载,作为友元时要注意传引用
Test& operator++(Test& test)
{
	++test.count;
        return *this;
}
  • RTTI

RTTI(Run Time Type Identification)即通过运行时类型识别,程序能通过积累的指针或引用来检查这些指针或引用指向的对象的实际派生类
c++为了支持多态,c++的指针或引用的类型可能与实际指向的对象类型不相同(如:用基类指针指向派生类的对象),这时就需要用rtti来判断实际类型了,RTTI是c++判断指针或引用的唯一方式

typeid函数:typeid函数返回的一个叫做type_info的结构体,该结构体包括了所指向对象的实际信息,其中name()函数就可以返回真实名称。

class Base
{
	virtual void func() {}
};

class Dev : public Base
{
};

int main()
{
	Base* p = new Dev();
	cout << typeid(*p).name() << endl;
	return 0;
//输出:class Dev

class Base
{
};
class Dev : public Base
{
};

int main()
{
	Base* p = new Dev();
	cout << typeid(*p).name() << endl;
	return 0;
}
//输出:class Base

/*
前提:用基类指针指向派生类对象
当父类中存在虚函数时  ,RTTI类型为子类类型
当父类中不存在虚函数时,RTTI类型为父类类型
*/

dynamic_cast<子类指针>(父类指针):将父类指针类型转换为为子类指针类型,转化成功返回指向子类的指针,失败返回nullptr,若转换的是引用类型,成功返回对应的引用失败抛出异常 std::bad_cast
二者常配合使用:

Base* p = new Dev();
if(string(typeid(*p).name()) == "class Dev")
//由于typeid().name返回const char* "class Dev"也为const char*,二者比较的是地址,而不是内容,故转为string类型,使内容相互比较
{
   Dev*ptr = dynamic_cast<Dev*>(p);
   if(ptr != nullptr)
    {
        cout<<"success"<<endl;
    }
}
  • 默认生成移动构造函数和移动赋值运算符的条件

只有一个类没有定义任何自己实现的拷贝操作(拷贝构造,拷贝运算符重载),且类的每个非静态成员都可以移动,系统才会生成。
可以移动的意思是可以进行移动构造,移动赋值,所用的基础类型都是可以移动的,有移动语义的类也是可以移动的。

对于普通成员包括指针变量采用等位拷贝,对于类成员调用其移动语义的构造或赋值重载,对于有指针成员的类来说,若使用默认生成的移动构造函数会导致double free