1、初始化列表初始化属性
语法结构:构造函数():属性1(值1),属性2(值2),属性3(值3)....{ }
上面就是初始化列表的结构,不适用这种方式,在构造函数中手动代码赋值也是可以的,只不过这样进行复制,代码更加的简洁。
calss person
{
public:
person(int m_a,int m_b,intm_c)
{
a=m_a;
b=m_a;
c=m_a;
}
int a;
int b;
int c;
}
int main(void)
{
person p1(10,20,30);
system("pause");
}
上面的代码,就是使用了有参构造函数进行属性的初始化。
calss person
{
public:
person():a(10),b(20),c(30)
{
}
int a;
int b;
int c;
}
int main(void)
{
person p1;
system("pause");
}
上面代码是使用初始化列表进行对象属性的初始化,这样的初始化使代码更简洁,但是赋值不太灵活。
calss person
{
public:
person(int m_a,int m_b,int m_c):a(m_a),b(m_b),c(m_c)
{
}
int a;
int b;
int c;
}
int main(void)
{
person p1(10,20,30);
system("pause");
}
上面的代码就是,就是使用初始化列表进行对象的属性的初始化,可以传入数值的初始化,这样的初始化更加灵活。
下图所示的,就是参数的复赋值路径方式。
一定要注意初始化列表的语法。
2、类对象作为类成员
c++类中的成员可以是另一个类的对象,我们称该成员为对象成员。使用初始化列表初始化成员对象的属性。
下面初始化列表的使用就会用到隐式转换法,下图是隐式转换的语法。
class phone
{
public:
phone(string name)//下面是隐式转换必要环节
{
P_name = name;
}
string P_name;
};
class person
{
//隐式转换法,phone M_phone= phone
//使用隐式转换法,类中的对象phone必须具有参函数才能使用,
// 不然就会报错。
public:
person(string name, string phone):M_name(name), M_phone(phone)
{
cout << "person的构造函数" << endl;
}
private:
string M_name;
phone M_phone;
};
void test1()
{
person p1("康","哑巴");
}
void main(void)
{
test1();
system("pause");
}
上面代码中,使用到了person的类内对象M_name,使用初始化列表初始化的时候,使用的是字符串name对M_name对象进行初始化,内部语法也就是隐式转换法。
上面类中的对象,对象和类创建的时候,首先创建phone类的M_phone的对象,然后在创建person类的p1对象。下面是代码的运行测试。
析构函数是首先调用person的析构函数,然后在调用phone的析构函数。下面是相应的代码和运行的结果:
class phone
{
public:
phone(string name)//下面是隐式转换必要环节
{
P_name = name;
cout << "phone的构造函数" << endl;
}
~phone()
{
cout << "phone的析构函数" << endl;
}
string P_name;
};
class person
{
//隐式转换法,phone M_phone= phone
//使用隐式转换法,类中的对象phone必须具有参函数才能使用,
// 不然就会报错。
public:
person(string name, string phone):M_name(name), M_phone(phone)
{
cout << "person的构造函数" << endl;
}
~person()
{
cout << "person的析构函数" << endl;
}
private:
string M_name;
phone M_phone;
};
void test1()
{
person p1("康","哑巴");
}
void main(void)
{
test1();
system("pause");
}
3、静态成员
静态成员变量:
静态成变量的知识主要有以下几点:
1、静态成员变量不属于某个类或者对象,是所有类和对象公用的一块内存区域。
2、静态成员变量的初始化是类内进行声明,类外进行访问的。
3、静态成员变量的访问在public作用域下的静态成员变量的访问有两种方式:一种是通过对象进行访问;另一种是通过类名进行访问。在private作用域下的静态成员变量是不能进行类外访问的,只能在类内进行访问。
4、private作用域下的静态成员变量也是需要在类外进行初始化的,只是不能在类外进行访问。
下面代码和运行结果是验证上面的知识点1、知识点4。
class person
{
public:
static int a;
private:
static int b;
};
int person::a=10;
int person::b = 10;
void main(void)
{
person p1;
cout << "p1.a:" <<p1.a<< endl;
person p2;
p2.a = 20;
cout << "p1.a:" <<p1.a<< endl;
system("pause");
}
下图是验证上面知识点3。(第一张图因为类外访问私有static变量产生错误)。
class person
{
public:
int c = 0;
static int a;
void set(int p_b)
{
b = p_b;
}
int get(void)
{
return b;
}
private:
static int b;
};
int person::a=10;
int person::b = 10;
void main(void)
{
/*person::c = 10;
person::set(10);*/
person::a = 30;
person p1;
p1.set(20);
cout << "p1.a:" <<p1.a<< endl;
cout << "p1.b:" << p1.get() << endl;
system("pause");
}
下图验证:因为类中的静态成员变量是在编译的时候就产生内存了,是放在全局区域的,所以外面可以直接通过类名调用类中的静态变量。类中的普通变量是在创建对象的时候才会产生这部分的内存,所以这部分的变量是在栈创建,不是全局区,所以不能通过类名修改类中的局部变量,只能通过创建对象之后,利用对象区修改,对象释放之后,相应的变量也会被清除。函数也是一样的。
静态成员函数
静态成员函数具有以下属性:
1、静态成员函数不属于某个对象或者类,所有的对象和类都是指向这一个函数的内存区域。
2、静态成员函数也是有访问权限:public下的静态成员函数可以通过(类名::静态成员函数名)进行访问,也可以创建一个对象,通过对象访问,但是这两种访问或者修改都是同一个静态成员函数;private下的静态成员函数,这个作用域下的静态成员函数只能在对象内或者类内进行访问。
3、静态成员函数只能访问静态的变量,原因:如果使用静态成员函数访问普通变量时,使用(类名::静态成员函数名)进行直接访问,这时候没有创建对象,那么对应普通变量也是没有分配内存,这时候访问就是错误的,当创建多个对象的时候,访问的时候并不知道访问那个对象的普通变量,这样也会产生错误。
下面验证上面的知识点1:类名::静态成员函数名的访问和通过对象访问的为同一个静态成员函数。
class person
{
public:
int c;
static int a;
static void set(int p_b)
{
a = p_b;
}
private:
static int b;
};
int person::a=10;
int person::b = 10;
void main(void)
{
person::set(30);
cout << "pperson.a:" << person::a << endl;
person p1;
p1.set(20);
cout << "p1.a:" << p1.a << endl;
system("pause");
}
下面验证上面的知识点2:类或者对象外调用静态成员函数,会报错。
下面验证上面的知识点3:静态成员函数访问了普通成员变量,产生错误。
4、成员变量和成员函数分开存储
C++中类内的成员变量和成员函数时分开存储的,只有非静态的成员变量才属于类的对象上,静态成员变量、(静态/非静态)成员函数都是不属于类的对象上。
进行程序验证之前,首先要知道,初始化的空类(也就是什么都没有的类)所占的内存时一个字节,如果类中有了成员变量,就会按照成员变量的大小进行分配内存。
5、this指针
通过上面的知识点,知道了成员变量和成员函数时被分开存储的,并且每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用 同一块代码。
那么这一块代码是如何区分那个对象调用的自己那?
c++中通过this指针来解决这个问题,this指针指向被调用的成员函数所属的对象。
this指针时隐含每一个非静态成员函数的一种指针。
this指针不需要定义,直接使用即可.
比如同一个类创建的多个对象,使用的是同一个成员函数,那么就是使用this指针指向调用这个函数的对象。
this指针的用途:
1、当形参和成员变量同名时,可用this指针来区分。
2、在类的非静态成员函数中返回对象本身,可使用return *this
首先验证利用this解决命名冲突,也就是上面的用途1:
从图中可以看出加了this指针之后,this指向的age与类中成员变量中的age是同一个变量。
下面的案例验证了上面this的用途2:
class person
{
public:
person(int age)
{
//this指针指向 被调用的成员函数所属的对象
this->age = age;
}
//采用引用的形式返回,引用的形式传参
person& PersonAddAge(person& p)
{
this->age += p.age;
//this指向 被调用的成员函数所属的对象
//*this就相当于这个对象本身
return *this;
}
int age;
};
void main(void)
{
person p1(10);
person p2(20);
//因为调用完对象函数之后,返回的是对象的引用
//所以可以重复调用
//p1.PersonAddAge(p2)==p1
p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);
cout << p1.age << endl;
system("pause");
}
下面是使用了值返回的形式,是新创建的一个对象,拷贝返回对象属性。
class person
{
public:
person(int age)
{
//this指针指向 被调用的成员函数所属的对象
this->age = age;
}
//采用值的形式返回,这时候会创建一个新的对象
person PersonAddAge(person& p)
{
this->age += p.age;
//this指向 被调用的成员函数所属的对象
//*this就相当于这个对象本身
return *this;
}
int age;
};
void main(void)
{
person p1(10);
person p2(20);
//因为调用完对象函数之后,返回的是新创建的一个副对象
//所以 p1.PersonAddAge(p2)==p1'
//p1'.PersonAddAge(p2)==p1''
//新创建的属性相同的对象
p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);
cout << p1.age << endl;
system("pause");
}
6、空指针
空指针调用的时候,会导致程序崩溃。下面程序中将介绍:
如果没有程序中的if(this==NULL)return;程序就会因为空指针,不能调用输出age属性导致程序崩溃。
class person
{
public:
void test1(void)
{
cout << "测试函数1,无参" << endl;
}
void test2(void)
{
if (this == NULL)//防止传入空指针导致程序崩溃
return;
//打印中的age的使用相当关于:this->age(知识省略了前缀)
cout << "测试函数2,有参" << age << endl;
}
int age;
};
void main(void)
{
person *p1 = NULL;
p1->test1();
p1->test2();
system("pause");
}
7、const修饰成员函数
常函数:
1、成员函数后夹const后我们称为这个函数为常函数
2、常函数内不可以修改成员属性
3、成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
1、声明对象前加const称该对象为常对象
2、常对象只能调用常函数
常函数:
加了mutable修饰之后,就是特殊变量,这时候就可以修改这个对象的属性值了。
如果使用常对象(属性和行为是不可以修改的),调用普通的成员函数(不是静态成员函数),那么就可以可以在这个普通的成员函数中对这个对象的属性进行修改,这就违背了常对象的初衷。
8、友元:全局函数做友元
首先介绍全局函数做友元,下面是实例代码,私有的属性,在对象外是不能进行调用的。
使用全局函数做友元,只需要将函数在类中进行声明,在前面添加上“friend”,这个函数就可以访问类的私有属性了。
class person
{
friend void test1(person* p);
public:
person()
{
name1 = "hong";
name2 = "daoyu";
}
public:
string name1;
private:
string name2;
};
void test1(person* p)
{
cout << "" << p->name2 << endl;
}
int main(void)
{
person p1;
cout << "" << p1.name1 << endl;
test1(&p1);
system("pause");
}
9、友元:类做友元
整体的使用原理就是:创建一个类(具有私有属性),在创建一个类(具有类内public域下的函数),类函数中读取另一个类private下的属性,这时候只需要在类中引用另一个类,加上friend的前缀。
下面代码使用类外对析构函数和类内函数的初始化。
class buliding;//这里是声明这个类的意思
class person
{
public:
person();
void vist();
private:
buliding* build;//前面声明过,所以这里可以使用
};
class buliding
{
friend class person;//类做友元的处理
public:
buliding();
private:
string b_name;
int b_age;
};
person::person()//类的析构函数的类外初始化,必须要类前缀
{
build = new buliding;
}
buliding::buliding()
{
this->b_name = "hong";
this->b_age = 24;
}
//类内的函数调用另一个类的私有属性
void person::vist()//类内函数的类外初始化
{
cout << build->b_name << ":" << build->b_age << endl;
}
int main(void)
{
person p1;
p1.vist();
system("pause");
}
10、友元:成员函数做友元
下面的案例是使用一个类中的函数作另一个类中的友元函数,然后利用这个函数访问另一个类中的private作用域下的属性。
class buliding;//这里是声明这个类的意思
class person
{
public:
person();
void vist();
private:
buliding* build;//前面声明过,所以这里可以使用
};
class buliding
{
friend void person::vist();//类中的函数做友元的处理
public:
buliding();
private:
string b_name;
int b_age;
};
person::person()//类的析构函数的类外初始化,必须要类前缀
{
build = new buliding;
}
buliding::buliding()
{
this->b_name = "hong";
this->b_age = 24;
}
//类内的函数调用另一个类的私有属性
void person::vist()//类内函数的类外初始化
{
cout << build->b_name << ":" << build->b_age << endl;
}
int main(void)
{
person p1;
p1.vist();
system("pause");
}