博主水平有限,不足之处如能斧正,感激不尽!
通过前两篇类和对象的讲解,我们对类和对象有了大概的认识,此篇主要是补充类和对象的其他语法,主要有:
我们提过,构造函数体内对属性的“初始化”实际上是赋值,到了函数体内,属性已经定义好了。那我们怎么能给属性真正初始化呢?
这就需要用到初始化列表。
初始化列表是对象的属性定义的地方。
某些属性必须初始化,而不是定义后赋初值。
如
className(datatype mem1, datatype mem2):_mem1(mem1),_mem2(mem2) {}
函数体前,用冒号开头,通过括号给成员初始化,成员的初始化用逗号隔开。
class B
{
public:B(int b){_b = b;}
private:int _b;
};class A
{
public:A(const int a1, int& a2, int b):_a1(a1), _a2(a2), _bb(b){}private:const int _a1;int& _a2;B _bb;
};int main()
{int a1 = 10;int a2 = 20;int b = 30;A aa(a1, a2, b);return 0;
}
如果不适用初始化列表初始化:
尽量都用初始化列表来初始化:用了一定没问题,不用可能有问题。
单参构造(只用传一个 参数)在类型合适的时候,会被赋值运算符重载触发 类型转换 的功能。
听着很抽象,结合explicit来理解。
explicit 是用来 禁用函数自动类型转换 的关键字
有时我们不想要这样触发的类型转换
explicit A() {//... }
接下来就看看,有无explicit修饰的构造有什么区别。
class A1
{
public:explicit A1(int a):_a(a){}
private:int _a;
};class A2
{
public:explicit A2(int a):_a(a){}
private:int _a;
};int main()
{//单参构造函数,用函数调用触发,不会发生类型转换A1 aa1(10);//单参数构造函数,用赋值运算符重载触发,会发生类型转换A2 aa2 = 20;//error:No viable conversion from 'int' to 'A2'return 0;
}
和整个类深度绑定的成员。
有些成员,需要能够被同类的所有对象访问。
static成员:
static成员变量
static成员函数
static成员变量:类内声明,类外初始化
class A
{
public:A(){//...cnt++;}A(const A& aa2){//...cnt++;}void Printcnt(){cout << cnt << endl;}
private:int _mem = 0;//计算调用了几次构造和拷贝构造static int cnt;
};int A::cnt = 0;A test(A aa)
{return aa;
}int main()
{A aa1;A aa2(aa1);test(aa1);aa1.Printcnt();return 0;
}:4
static成员函数
class A
{
public:static void Print(){cout << "className: A" << endl;}
private:int _mem = 0;
};int main()
{A aa1;A aa2;A aa3;aa1.Print();aa2.Print();aa3.Print();return 0;
}:className: A
className: A
className: A
:不可以,没有this指针,非静态成员函数的第一个this形参接收不到。
可以,静态成员函数属于整个类,类外指定类域即可调用,类内的成员函数更是可以直接调用。
this指针具有常性的成员函数。
带上const对象一起玩。
默认情况下,this的类型是 classname* const,尽管它是隐式传递的,也还是要遵循初始化规则,这也代表,当对象为常量对象,this指针传参会不匹配:
const classname* const 传给 classname* const
所以引入了const成员函数,
来声明此函数的this是const className* const。
void Print() const {//... }
放在参数列表后面,表示此成员函数的this指针为 const className* const this。
不用改变对象的函数全声明成const成员函数。
有些时候,我们实现函数时,需要在类外访问类内的私有成员,但是又不想通过成员函数的形式,就有了友元。
是对非成员函数对类成员的**访问权限的声明(**声明后可以访问类内成员)。
虽然是满足了需要,但是这也破坏了类的封装,尽量少用。
有时需要允许特定的函数访问私有成员。
class A
{
public:friend void PrintMem(A& aa);
private:int _mem = 0;
};void PrintMem(A& aa)
{cout << aa._mem << endl;
}int main(int argc, const char * argv[])
{A aa;PrintMem(aa);return 0;
}:0
下图就印证了:友元函数的声明仅仅是声明了“函数的访问权限”,而非“对函数本身的声明”。
基本和友元函数一样。
class A
{
public:friend class B;
private:int _mem = 10;
};class B
{
public:void PrintA(){cout << _aa._mem << endl;}
private:int _mem = 0;A _aa;
};int main(int argc, const char * argv[])
{B bb;bb.PrintA();return 0;
}
类中类,但它是独立的类。
class A
{
public:class B{public:void Print(A& aa){cout << "内部类B访问外部类A的static成员:" << _a1 << endl;cout << "内部类B访问外部类A的普通成员:" << aa._a2 << endl;}private:int _b = 30;};private:static int _a1;int _a2 = 20;
};int A::_a1 = 10;int main()
{A aa;A::B bb;bb.Print(aa);return 0;
}:内部类B访问外部类A的static成员:10
内部类B访问外部类A的普通成员:20
破坏封装,尽量少用。
没有名字,只在当前行生效(下一行前自动调用析构)的对象。
有时候,我们实例化一个对象仅仅只是为了调用它的函数或其他简单的操作,这时候要实例化出一个普通对象,用完还要等到出生命周期才调用析构——不如弄个临时的用用。
class A
{
public:void Print(){cout << _aa << endl;}~A(){cout << "~A()" << endl;}
private:int _aa;
};int main()
{A().Print();cout << "------------" << endl;return 0;
}:10
~A()
------------
今天的分享就到这里啦
这里是培根的blog,期待与你共同进步!
下期见~
六、匿名对象
没有名字,只在当前行生效(下一行前自动调用析构)的对象。
有时候,我们实例化一个对象仅仅只是为了调用它的函数或其他简单的操作,这时候要实例化出一个普通对象,用完还要等到出生命周期才调用析构——不如弄个临时的用用。
class A
{
public:void Print(){cout << _aa << endl;}~A(){cout << "~A()" << endl;}
private:int _aa;
};int main()
{A().Print();cout << "------------" << endl;return 0;
}:10
~A()
------------
今天的分享就到这里啦
这里是培根的blog,期待与你共同进步!
下期见~