//举例-圆类
class Circle
{
public:
//属性-成员变量
int m_r;
//行为-函数
double calculateZC()
{
return 2 * PI * m_r;
}
}
int main()
{
Circle c1;
c1.m_r = 10;
cout << "圆的周长" << c1.calculateZC() << endl;
return 0;
}
三种:
public | 公共权限 | 成员 | 类内可以访问、类外也可以访问 | |
protected | 保护权限 | 成员 | 类内可以访问、类外不可以访问 | 继承中,儿子可以访问父亲的保护内容 |
private | 私有权限 | 成员 | 类内可以访问、类外不可以访问 | 继承中,儿子不可以访问父亲私有的内容 |
class C1
{
int m_A;//默认为private
}
struct C2
{
int m_B;//默认为public
}
优点:将所有成员属性设置为私有,可以自己控制读写权限(也就是在类内根据权限控制要求,提供相应的方法去获取或修改私有属性,就是java的get、set方法)
class person
{
private:
int m_Age;
String m_name;
String m_Lover;
public:
void setAge(int m_Age)
{
m_Age = m_Age;
}
int getAge()
{
return m_Age;
}
void setLover(String Lover)
{
m_Lover = Lover;
}
int getName()
{
return m_Name;
}
}
优点:可以检测数据的有效性
class person
{
int m_Age;
public:
void getAge(int age)
{
if(age < 0 || age > 150)
{
cout << "error" << endl;
return;
}
m_Age = age;
}
}
构造函数:主要用于在创建对象时为对象的成员属性赋值,该函数无需手动调用,由编译器自动调用。
析构函数:主要用于在对象销毁前系统自动调用,执行一些清理操作。
两种分类方式:
按参数划分:有参构造和无参构造
按类型划分:普通构造和拷贝构造
Example.h代码
class Example
{
public:
Example();
Example(const Example& example);
~Example();
public:
int a;
};
Example.cpp代码
Example::Example()
{
a = 10;
cout << "普通构造-无参构造" << endl;
cout << "a = " << a << endl;
}
Example::~Example()
{
}
//拷贝构造
Example::Example(const Example &example)
{
a = example.a;
cout << "拷贝构造" << endl;
}
main代码
int main()
{
Example e = Example();
Example f = Example(e);//拷贝构造
cout << f.a << endl;
}
运行截图:
三种调用方式:
注意:不要用拷贝构造初始化匿名对象 编译器认为是对象的声明
//就是拷贝构造
class Person
{
public:
int age;
}
void test01()
{
Person p1 = Person(20);
Person p2 = Person(p1);
}
//就是对象作为函数形参时,在被调函数中会通过拷贝构造去拷贝一个新的对象在被调函数
//中使用
void func(Person p)
{
}
void test02()
{
Person p1 = Person(20);
func(p1);
}
//就是通过拷贝构造去返回对象
Person func()
{
Person p1 = Person(20);
return p1;
}
void test03()
{
Person p1 = func();
}
默认情况下,编译器会提供三个构造函数,即无参构造、有参构造、拷贝构造
若用户主动提供有参构造,则编译器不会提供无参构造,但提供拷贝构造
若用户主动提供拷贝构造,则编译器不会提供无参构造和有参构造
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
案例:对于一个类,其中有int m_a;和 int *m_b;两个成员变量,对于m_b而言是通过有参构造即m_b = new int(b);其中b为传入值来赋值的,因此在堆区,需要通过析构函数进行手动释放,若直接采用编译器提供的拷贝构造函数来进行调用,则先释放拷贝函数构造的对象时,m_b的空间即被释放了,会导致原来的对象无m_b指向的空间去释放。因此产生下图错误。
//案例代码
//Example.h code
class Example
{
public:
Example();
Example(int a,int b);
//Example(const Example& example);
~Example();
public:
int m_a;
int* m_b;
};
//Example.cpp code
#include "Example.h"
Example::Example(int a,int b)
{
m_a = a;
m_b = new int(b);
cout << "普通构造-有参构造" << endl;
cout << "a = " << m_a << endl;
cout << "a = " << *m_b << endl;
}
Example::~Example()
{
if (m_b != NULL)
{
delete(m_b);
m_b = NULL;
}
}
//main函数
void test()
{
Example e = Example(4,5);
Example f = Example(e);//拷贝构造
}
int main()
{
test();
}
解决方法:深拷贝即重写拷贝构造将编译器提供的构造语句
m_b = example.m_b;
修改为重新获得一个内存区域存放值,并将m_b指向该区域。
Example::Example(const Example& example)
{
m_a = example.m_a;
m_b = new int(*example.m_b);//修改的语句,实现深拷贝
cout << "拷贝构造" << endl;
}
语法:构造函数()属性1(值1),属性2(值2)...{}
//传统有参构造赋初值
Example(int a,int b,int c)
{
m_a = a;
m_b = b;
m_c = c;
}
//初始化方法为属性赋初值
Example(int a, int b ,int c):m_a(a),m_b(b),m_c(c)
{
}
注:
1.对于初始化列表要注意的是其效果相当于在构造函数内进行赋值,但不完全等同,若
const 修饰的变量其不能通过赋值来初始化,因此只能通过初始化列表的方式。
若普通的变量,初始化列表和构造函数内赋值效果相同。
class myClass { public : myClass();// 构造函数,无返回类型,可以有参数列表,这里省去 ~myClass();// 析构函数 int a; const int b; } myClass::myClass():a(1),b(1)// 初始化列表 { }
并且对于
引用变量(引用是一种别名,它允许我们使用一个已存在的对象来创建一个新的名称。引用变量在声明时必须进行初始化,并且一旦初始化后,它将一直引用同一个对象,无法改变引用的目标。)也需要创建时就进行初始化
class Example
{
public:
Example(int &_ref);
~Example();
public:
const int m_c;
int & ref;
}
Example::Example(int &_ref):m_c(1),ref(_ref)
{
}
//mian函数
void main()
{
int c = 10;
Example e = Example(c);
c = 20;
cout << e.m_c << "ref" << e.ref << "c:" << c << endl;
}
运行截图:
2.初始化时要和参数的顺序一致,不然出现赋值错乱。
主要关注谁先调用和销毁
当其他类作为本类的成员变量时,
构造时,先构造其他对象再构造自身
析构时,先析构自身再析构其他对象
静态成员就是在成员变量和成员函数前加上关键字static,包括静态成员属性和静态成员函数
1.所有对象共享同一份数据
2.在编译阶段分配内存
3.类内声明,类外初始化
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
静态成员代码示例:
//Example.h
class Example
{
public:
static int m_A;//静态成员变量-公有
private:
static int m_B;//静态成员变量-私有
};
//Example.cpp
无
//注意一定在类外给静态成员变量进行初始化,不能在另一个类中,在main之前可以
//mian
int Example::m_A = 10s;
int main()
{
cout << Example::m_A << endl;
Example e;
e.m_A = 20;
cout << e.m_A << endl;
cout << Example::m_A << endl;
system("pause");
return 0;
}
运行结果:
静态成员函数代码示例:
//Example.h
class Example
{
public:
static void func();
public:
static int m_A;//静态成员变量-公有
private:
static int m_B;//静态成员变量-私有
};
//Example.cpp
void Example::func()
{
cout << "static function 被调用了" << endl;
m_A = 100;//只能访问静态成员变量
cout << "static function 调用了静态成员变量并修改为100,m_A=" << m_A << endl;
}
//mian
int main()
{
cout << Example::m_A << endl;
Example e;
e.m_A = 20;
cout << e.m_A << endl;
cout << Example::m_A << endl;
Example::func();
e.func();
system("pause");
return 0;
}
运行截图:
为什么静态函数只能访问静态变量?因为静态成员变量和静态成员函数是在对象创建之前就已经分配了空间所确定的,假设静态成员函数访问非静态成员,则有可能非静态成员还没有被创建,肯定就无法访问咯。还有一种解释,因为所以对象共用静态成员函数,可能调用非静态成员变量时并不知道访问哪一个对象的非静态变量的值,这不就矛盾了那也不能访问咯。
若创建一个空对象,对象内既无成员变量也无成员函数则其所占内存为sizeof 为 1
非静态成员变量属于该类的对象上
静态成员变量不属于该类的对象上
非静态成员函数不属于类对象上
静态成员函数不属于该类对象上
this指针指向的是被调用的成员函数所属的对象 this-> 注意是:->
return *this; 表示返回的是当前被调用的对象
//一、为了防止重名
类中有成员变量age
Example::Example(int age)
{
this->age = age;
}
//*this返回对象
//Example.h
class Example
{
Example(int age);
Example& add_age_ref(Example p);
Example add_age_unref(Example p);
int age;
};
//Example.cpp
Example& Example::add_age_ref(Example p)
{
// TODO: 在此处插入 return 语句
this->age += p.age;
return *this;
}
Example Example::add_age_unref(Example p)
{
this->age += p.age;
return *this;
}
//mian
int main()
{
Example e = Example(18);
cout << "this->age:" << e.age << endl;
Example *e1 = new Example();
e1->add_age_unref(e).add_age_unref(e).add_age_unref(e);
cout << "返回为Exaple &类型的age调用add_age_unref()函数二次后" << e1->age << endl;
e1->add_age_ref(e).add_age_ref(e).add_age_ref(e);
cout << "返回为Exaple &类型的age调用add_age_ref()函数二次后" << e1->age << endl;
system("pause");
return 0;
}
运行结果:
new对象和构造对象的区别:
new的对象是在堆区,因此需要手动管理和释放,而构造产生的对象是在栈,有析构函数进行删除,根据作用域范围,超出自动删除。
->和.的区别:
指针用->,对象用.
返回对象Example 与 Example & 的区别:
对于Example而言返回的时候是重新copy了一个新的进行返回,其实编译器支持 C++11 及以上的标准,并且对象具有可移动语义(例如,具有移动构造函数和移动赋值运算符),那么返回一个对象时,编译器可能会使用移动语义而不是拷贝构造函数,从而减少对象的复制。因此此时
e1->add_age_unref(e).add_age_unref(e).add_age_unref(e);始终是新的对象,则并不会叠加age的值,即是初始值18.
而对于Example &而言返回的是同一个对象,因此此时
e1->add_age_ref(e).add_age_ref(e).add_age_ref(e);是同一个对象,age变会累加四次,即是72.
空指针对象可访问成员函数,但不能访问成员变量。由于访问时其实是通过当前对象的this指针去访问,空指针对象的this仍然为空,因此不能访问成员变量。例如调用一个只进行打印输出的成员函数是没问题的。
常函数:
.h
.cpp
常对象:
const对象只能调用常函数,只能访问mutable成员变量。
目的就是让一个函数或者一个类访问另一个类中的私有成员- 友军[旺柴]
友元关键字:friend
只需要将该全局函数在类中声明为友元即可
核心:friend void fun();
.h
class ClassAndObject
{
friend void AHUT();public:
void printf_message() const;
void print_message_normal();
public:
int m_a;
mutable int m_b;
private:
string m_lab;};
.main
void AHUT()
{
ClassAndObject co;
co.m_lab = "服了";
string s = co.m_lab;
cout << "ClassAndObject类的友元函数AHUT中的私有成员变量m_lab:" << s<< endl;
}int main()
{AHUT();
system("pause");
return 0;}
运行截图:
只需要将主动访问类在被访问类中声明为友元即可
核心:friend class goodMan;//goodMan即为主动访问的类
代码示例:Example类去调用ClassAndObject类中的私有成员变量;
ClassAndObject.h
class ClassAndObject
{
friend class Example;
private:
string m_lab;};
ClassAndObject.cpp
//构造方法
ClassAndObject::ClassAndObject()
{
m_lab = "ClassAndObject类中的私有成员变量m_lab";
}Example.h
class Example
{
public:
void use_ClassAndObject();
ClassAndObject m_co;
};Example.cpp
void Example::use_ClassAndObject()
{
cout << m_co.m_lab << endl;
}mian
int main()
{Example e = Example();
e.m_co = ClassAndObject();e.use_ClassAndObject();
system("pause");
return 0;}
运行截图: