目录
一、C++类
1.1 访问权限(跟java和python一样)
class Person
{
public: // 类内类外访问
private: // 私有,类内才能访问
protected:// 子类可以访问
};
1.2 struct和class的区别
struct默认权限是public,class默认权限为private
1.3 C++的构造函数和析构函数
class Person
{
public:
Person() /* C++构造函数跟java一样,类名做构造函数,可有参数(python是__init__);
不写的时候编译器会默认添加构造函数和析构函数,但是是个空函数 */
{
cout << "无参构造函数调用" << endl;
}
Person(int a) // 有参
{
age = a
cout << "有参构造函数调用" << endl;
}
Person(const Person & P) // 拷贝构造函数
{
age = P.age
cout << "拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl; /* 在对象销毁前会自动调用析构函数,调用test()结束后P会被回收,调用析构函数
Java不需要析构函数因为java有自动内存回收机制 */
}
void ageOut(void)
{
cout << "age:" << age << endl;
}
int age;
};
void test()
{
Person P1;
Person P2(10);
Person P3(P2);
P3.ageOut;
}
int main()
{
test(); // "构造函数调用" /n "Person析构函数调用"
}
1.4 成员变量类型
类内静态变量需要再类内定义,但是要在类外进行初始化。
类内的静态变量和静态函数是不属于该类实例化的对象的,并且可以直接使用类名进行调用。
对于在类中声明的函数(包括构造函数),可以在类外进行初始化,包括构造函数
#include<iostream>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */
class Phone
{
public:
Phone()
{
cout << "Phone构造函数调用" << endl;
}
~Phone()
{
cout << "Phone析构函数调用" << endl;
}
};
class Person
{
public:
Person()
{
cout << "Person构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
Phone my_phone; /* 成员变量为其他类,构造函数和析构函数的调用顺序问题 */
};
class PersonS
{
public:
PersonS(int age_in)
{
age = age_in;
// person_age = age_in; /* 这样初始化类内静态变量会报错*/
}
void TestOutInit(void);
static void func(int fault_flag)
{
cout << "static func函数调用" << endl;
// if(fault_flag) age = 0; /* 类内静态函数不能访问其他变量,我以为调用的时候会出错,其实编译的时候就会报错,所以必须注释掉 */
person_age = 0; /* 但是可以访问类内静态变量*/
}
int age;
static int person_age; /* 成员变量为静态变量,需要在类内声明,类外初始化 */
};
int PersonS::person_age = 1; /* 类外初始化class内静态变量 */
void PersonS::TestOutInit(void) /* 类外初始化类中声明的成员函数 */
{
cout << "调用类外初始化的函数" << endl;
}
/* 测试类内变量有其他对象时的构造函数䄦析构函数调用顺序 */
void test(void)
{
Person p1;
}
/* 测试类中静态变量/静态函数的调用方法 */
void test2(void)
{
PersonS ps1(10);
cout << "类外初始化后的person_age: " << ps1.person_age << endl;
PersonS ps2(20);
ps2.person_age = 50;
cout << "p2对person_age修改后,p1.person_age: " << ps1.person_age << endl;
/* 类的静态变量/函数不属于某个对象,相当于实例化的对象访问这个静态变量时都在访问同一个地址中的数据 */
cout << "通过类名访问person_age: " << PersonS::person_age << endl;
printf( "通过类名访问func(): " );
PersonS::func(0);
cout << "通过类名访问func()后修改了person_age: " << PersonS::person_age << endl;
ps1.TestOutInit();
}
int main(void)
{
test(); /* Phone构造函数调用 Person构造函数调用 Person析构函数调用 Phone析构函数调用 */
test2();
return 0;
}
二、类特性(一)
2.1 成员变量和成员函数内存分配
如果类为空,实例化对象时仍然会分配一字节的内存,为了区分各个实例化对象的存储位置
在C++类中,实例化对象时只会给非静态成员变量分配内存,函数和静态变量都不会被分配内存
#include<iostream>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */
/* 空类 */
class PersonNull
{
};
class Person
{
public:
int age; /* 非静态变量会在实例化对象时给实例化对象分配该变量的内存 */
static int person_age;; /* 静态变量则不会在实例化时分配内存 */
void PersonFunc() /* 也不会给实例化对象分配成员函数的内存 */
{
}
};
/* 测试在实例化对象时内存分配情况 */
void test(void)
{
PersonNull pn1;
Person p1;
cout << "PersonNull实例化对象pn1大小: " << sizeof(pn1) << endl; /* 就是类为空,也会给实例化对象分配一字节内存,以区分实例化对象存放的位置 */
cout << "Person实例化对象p1大小: " << sizeof(p1) << endl; /* 可以看到,没有给实例化对象的函数和静态变量分配内存,只给类内int非静态变量分配了内存 */
}
int main(void)
{
test(); /* Phone构造函数调用 Person构造函数调用 Person析构函数调用 Phone析构函数调用 */
return 0;
}
2.2 this指针与类空指针
通过this指针和类空指针可以更好的理解C++的一些特性,当指向NULL的class类型指针在调用该类的成员函数时是可以调用的,这个特性本质是因为C++是静态绑定
,这是C++编译器为了节约资源而设计的。在编译时将类的函数单独放在一块内存中
,而不是在实例化对象时,将函数放在该实例化对象的内存区域中,所以一个类的所有实例化对象调用类中定义的函数时其实都是调用的同一个函数
(同一地址下的代码)。
那实例化对象的内存中存放的是什么呢?存放的是该类下定义的成员变量
。一个类中还会定义一个隐藏变量:this
。this 是 C++ 中的一个关键字,是一个const指针,它指向当前对象(但是不存储在该对象中,并且只能在成员函数内部使用)。在实例化对象中调用成员变量时,实际上是在执行this->成员变量
。
下面代码函数Test2在初始化CPerson类型指针p_p1指向NULL时,隐藏成员变量this也指向了NULL
(具体this什么时候初始化的还没研究)。调用类中定义的函数时,与该指针指向的地址没有关系,反正都会去执行在编译时就给类函数分配好的地址中的代码。但是如果有的函数用到了该类的成员变量,就会报错,因为类的成员变量是在实例化对象的内存区域中的,而this执向了NULL
,就找不到这个成员变量了。
这么一看C++效率确实比java和python高,就是学起来有点绕
#include<iostream>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */
class CPerson
{
public:
CPerson(int age)
{
this->age = age;
}
CPerson& AgeAdd(int age)
{
this->age += age; /* 与python中的self有异同,在调用成员变量时没有区别,但是在调用成员函数时不一样 */
return *this; /* 返回该对象 */
}
void NullTest1(void)
{
cout << "这个func不调用内部变量" << endl;
}
void NullTest2(void)
{
cout << "这个func调用内部变量 " << this->age << endl; /* 这个就会报错*/
}
int age;
};
/* 测试this用法 */
void Test1(void)
{
CPerson p1(10);
cout << "p1.age: " << p1.age << endl;
p1.AgeAdd(10).AgeAdd(10).AgeAdd(10); /* 链式思想,p1.AgeAdd()会return *this 相当于返回了p1 */
cout << "现在p1.age: " << p1.age << endl;
}
/* 测试空指针特性 */
void Test2(void)
{
CPerson * p_p1 = NULL;
p_p1->NullTest1(); /* 可以正常执行 */
// p_p1->NullTest2(); /* 报错 */
}
int main(void)
{
Test1();
Test2();
return 0;
}
2.3 const修饰成员函数、实例化对象
没什么好说的,就是const修饰函数时写的位置,以及就算使用const修饰成员函数,该函数仍然可以修改mutable类型变量
但是在代码运行过程中遇到一个bug: error: uninitialized const 'p2' [-fpermissive]
error: uninitialized const 'p2' [-fpermissive]
note: 'const class CPerson_2' has no user-provided default constructor
note: and the implicitly-defined constructor does not initialize 'int CPerson_2::mul_phone'
是因为如果const修饰的实例化对象里面如果有成员变量的话,必须要有合适的构造函数可以初始化这些成员变量。没有构造函数的话就会报错
#include<iostream>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */
class CPerson
{
public:
void ConstTest1(void)
{
age = 10;
}
/* 在函数右边加了const之后就相当于CPerson * const this --> const CPerson * const this
原来只是this是个const(this指针的指向不可以修改),现在又加了一条:this指向的地址中的值也不能修改 */
void ConstTest2(void) const
{
// age = 50; /* 如果上面函数加了const,就不允许修改这个age,否则会报错 */
mul_phone = 10086;
cout << "现在的age和mulphone: " << age << " " << mul_phone << endl;
}
int age;
mutable int mul_phone; /* 但是使用mutable修饰的变量,仍可以修改 */
};
class CPerson_2
{
public:
/* 这里有个需要注意的地方,如果const修饰的实例化对象里面有成员变量,那么构造函数中必须存在合适的构造函数对该成员变量初始化 */
CPerson_2(int phone_number)
{
mul_phone = phone_number;
cout << "mulphone: " << mul_phone << endl;
}
void ConstTest1(void) const
{
mul_phone = 10086;
cout << "mulphone now: " << mul_phone << endl;
}
mutable int mul_phone;
};
/* 测试this用法 */
void Test1(void)
{
CPerson p1;
p1.ConstTest1();
p1.ConstTest2();
const CPerson_2 p2(10000);
p2.ConstTest1(); /* 报错,使用const修饰的实例化对象只能调用const修饰的成员函数 */
}
int main(void)
{
Test1();
return 0;
}