- struct person
- {
- char name[16]; //名字
- float height; //身高
- float height; //体重
- char cell[32]; //手机号
- };
- class person
- {
- private:
- char name[16]; //名字
- float height; //身高
- float weight; //体重
- char cell[32]; //手机号
- public:
- int running(char *WayName,int time); //跑步行为
- int eat(char *ObjectName,int l); //存放行为
- };
- #include
- #include
- using namespace std;
- class person
- {
- private:
- char name[16]; //名字
- float height; //身高
- float weight; //体重
- char cell[32]; //手机号
- public:
- person();
- person(const char *ne,float h,float w,const char *ce);
- ~person()
- {
- cout << name <<":执行了析构" << endl;
- }
- void run()
- {
- cout << "名字:"<< name << endl;
- cout << "身高:"<< height << endl;
- cout << "体重:"<< weight << endl;
- cout << "手机号:"<< cell << endl;
- }
- int running(char *WayName,int time); //跑步行为
- int eat(char *ObjectName,int l); //存放行为
- };
- person::person()
- {
- strcpy(name,"zhangsan");
- height = 168.4;
- weight = 64.2;
- strcpy(cell,"17873432557");
- }
- person::person(const char *ne,float h,float w,const char *ce)
- {
- strcpy(name,ne);
- height = h;
- weight = w;
- strcpy(cell,ce);
- }
- int main()
- {
- person a; //创建一个对象,执行的是没有参数的构造函数
- person b{"jiuyue",178.7,76.6,"110"}; //创建一个对象执行有参数的构造函数
- a.run();
- b.run();
- return 0;
- }

person a ();
person a ;// 或person a {};
- class CExample
- {
- public:
- int a;
- float b;
- //构造函数初始化列表
- CExample(): a(0),b(8.8)
- {}
- //构造函数内部赋值
- CExample()
- {
- a=0;
- b=8.8;
- }
- };
我们来看第一种情况为什么要使用构造函数初始化列表:(成员对象没有默认构造函数)
- #include
- #include
- using namespace std;
- class A
- {
- private:
- int a;
- char b;
- public:
- A(int x,char y)
- {
- a = x;
- b = y;
- cout << "a 的构造" << endl;
- }
- };
- class B
- {
- private:
- A a;
- int c;
- public:
- B(int z,char n,int x):a(z,n){ c = x;}
- };
- int main(int argc, char const *argv[])
- {
- B b(1,'2',3);
- return 0;
- }
- #include
- #include
- using namespace std;
- class A
- {
- private:
- int a;
- const int b;
- char& c;
- public:
- A(int x,char y):b(x),c(y)
- {
- a = 3;
- //b = 4; //const 常量只能在声明时初始化,之后不能改变
- //c = ? //引用只能在声明的时候进行初始化
- }
- };
- int main(int argc, char const *argv[])
- {
- char i = '1';
- A(4,i);
- return 0;
- }
- #include
- #include
- using namespace std;
- class A
- {
- private:
- int a;
- const int b;
- char& c;
- public:
- A(int x,char y):b(x),c(y)
- {
- a = 3;
- //b = 4; //const 常量只能在声明时初始化,之后不能改变
- //c = ? //引用只能在声明的时候进行初始化
- }
- };
- class B:public A
- {
- private:
- int x;
- public:
- B(int a1,char b1):A(a1,b1)
- {
- x = 2;
- }
- };
- int main(int argc, char const *argv[])
- {
- char i = '1';
- B(4,i);
- return 0;
- }
- int a = 3;
- const char c = '1';
- A b(2,5);
- int a;
- const char c;
- A b;
- a = 3;
- c = '1' //error
- b(3,5); //error
例如:
- #include
- #include
- using namespace std;
- class A
- {
- private:
- int a;
- int b;
- public:
- A(int x,int y):a(x),b(y)
- {
- cout << "构造" <
- }
- ~A()
- {
- cout << "析构" <
- }
- A fun()
- {
- return A(a,b);
- }
- };
- int main(int argc, char const *argv[])
- {
- A i = A(1,2);
- A j = i.fun();
- return 0;
- }
直接调用构造函数将产生一个临时对象。
临时对象的声明周期只有一条语句的时间。
临时对象的作用域只在一条语句中。
临时对象是 C++中值得警惕的灰色地带。
类的只读成员函数
在 c++中我们知道 const 可以用来指定变量为只读变量,那么在 C++中,我们还可以使用 const 来修饰类中的成员函数,例如:
- class A
- {
- private:
- int a;
- int b;
- public:
- A(int x,int y):a(x),b(y){}
- int fun() const
- {
- //...
- }
- };
如上程序,类中的 fun()函数被 const 修饰后,那么在 fun()函数中将不能对类的成员变量进行改变。这就是 const 修饰类中成员函数的作用。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,
是使用同一类中之前创建的对象
来初始化新创建的对象
。拷贝构造函数通常用于:
•通过使用另一个同类型的对象来初始化新创建的对象。
•复制对象把它作为参数传递给函数。
•复制对象,并从函数返回这个对象。
函数形式:
类名
(const
类名
&)
;
注意:
在类中有一个默认的赋值操作,容易和拷贝构造搞混淆,赋值操作是当=
两边都是已经初始化好的对象进行赋值时才会调用赋值函数,而当用一个已经初始化好的对象去初始化另一个对象时,那么另一个对象就调用的是拷贝构造函数。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
- class A
- {
- private:
- int a;
- int b;
- public:
- A(int x,int y):a(x),b(y){} //构造函数初始化列表
- A(const A &obj) //拷贝构造函数
- {
- // 构造函数的主体
- }
- };
当对对象进行拷贝赋值时就会调用拷贝构造函数进行初始化赋值,如
- int main(int argc, char const *argv[])
- {
- A i(1,2);
- A j = i; //调用了拷贝构造
- A k(i); //调用了拷贝构造
- return 0;
- }
省略复制
复制省略是自 C++11
标准起提出的一项编译优化技术,通过省略复制及移动构造函数的调用,实现零复制的值传递语义。省略复制一般默认存在,可以通过在编译时添加-fno-elide
constructors
选项来关闭省略复制
。在以下两种情况中会出现省略复制。
1.
在变量初始化中,当初始化表达式
(
右值
)
与变量类型为同一类型的纯右值
(
临时量
)
时,
例如:
- #include
- #include
- using namespace std;
- class A
- {
- private:
- char buf[16];
- public:
- A(const char *str)
- {
- strcpy(buf,str);
- cout << "构造函数" << endl;
- }
- A(const A& a)
- {
- strcpy(this->buf,a.buf);
- cout << "拷贝构造" << endl;
- }
- };
- int main()
- {
- A ob = "nihao";
- return 0;
- }

上面是进行了省略构造后的结果,那关闭省略构造呢?

可以看到,不进行省略复制就会执行拷贝构造函数,省略复制相当于把 A ob = “nihao”
变成了 A ob(
“
nihao
”
);
直接将对象构造到它们本来要拷贝的存储空间
,
而不进行省略复制,
A ob = “nihao”相当于
A ob = A(“nihao”)
;
2.
第二种情况是在
return
语句中,操作的对象是与函数返回类型为同一类型的纯右值
(
临时量)
时,例如:
- #include
- #include
- using namespace std;
- class A
- {
- private:
- char buf[16];
- public:
- A(const char *str)
- {
- strcpy(buf,str);
- cout << "构造函数" << endl;
- }
- A(const A& a)
- {
- strcpy(this->buf,a.buf);
- cout << "拷贝构造" << endl;
- }
- };
- A fnn()
- {
- return A("nihao");
- }
- //或
- A fuu()
- {
- A a("nihao");
- return a;
- }
- int main()
- {
- A ob = fuu();
- return 0;
- }

上图是进行了省略复制的结果

上图是不进行省略复制的结果,可以看到,原本的 A ob = fuu();
在不进行省略复制的情况 下,编译器会被解释为 A ob = A(fuu());
在 C++
中,每一个对象都能通过
this
指针来访问自己的地址。
this
指针是所有成员函数的隐含参数(
也就是所有成员函数都默认有一个隐藏的指针参数保存着对象的地址
)
。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有 this
指针,因为友元不是类的成员。只有成员函数才有
this
指针。静态成员函数也没有 this
指针,因为静态成员函数不属于某个对象,只属于类本身。
- class A
- {
- private:
- int a;
- int b;
- public:
- A(int x,int y):a(x),b(y){} //构造函数初始化列表
- void fun()
- {
- this->a = 4; //this 就是该对象的地址
- (*this).b = 5; //*this 就相当于对象本身
- }
- };
如上,我们可以在成员函数中利用 this 来操作成员变量,至于这个 this 是谁的地址,那就要看是哪个对象了,例如:a.fun();那么 this 就指向 a 这个对象,b.fun();那么 this 就指向 b 这个对象,this 其实用处很多,如下面程序:
- class A
- {
- private:
- int a;
- int b;
- public:
- A(int x,int y):a(x),b(y){} //构造函数初始化列表
- void fun(int a,int b)
- {
- //a = a; //erreor ,系统会以为是形参 a 赋值给形参 a,并不会改变成员变量的值
- //b = b; //同上理
- this->a = a;
- this->b = b;
- }
- };
当参数的名字和成员的名字相同时,那么就必须使用 this 去做区分了。
总之,我们理解 this 是成员函数中隐藏的一个指向对象地址的一个指针就可以了,就相当于原本类中的函数应该都是下面这样的:
void
sun
(
A
*
this
);
void
fun
(
int
a
,
int
b
,
A
*
this
);
每个函数中都必须有一个类指针 this,当调用它时,就会默认传入调用者对象的地址,如 下:
A
a
(
1
,
2
);
a
.
sun
(&
a
);
a
.
fun
(
1
,
2
,&
a
);
但是,你可以理解为系统将这些东西都隐藏了。
静态成员函数是使用 static 修饰的成员函数,只能被定义一次,而且要被同类的所有对象所共享,它是类的一种行为,与对象无关。
- class person
- {
- public:
- static void func(); //只需要在类内进行声明
- };
它有如下特点:
1.
静态成员函数不可以直接访问类中非静态数据成员以及非静态成员函数,只能通过对象名(由参数传入)来访问,即在类的静态成员函数(类的静态方法)内部,不能直接访问 this 指针和对象的数据成员,在类的静态成员函数(类的静态方法)内部,只能访问类的静态数据
成员!
2.
静态成员函数在类外定义时,无需加
static
修饰,否则出错;

3.
在类外,可以通过对象名以及类名来调用类的静态成员函数和变量
4.
对象和类可以直接访问静态成员函数,静态成员之间在类中可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
5.
在类外,可以通过对象名以及类名来调用类的静态成员函数,静态成员函数不能访问非静态成员函数和非静态数据成员;
6.
声明静态成员函数时,不可同时声明为
virtual
、
const
、
volatile
函数
7.
静态函数可以使用作用域标识符直接访问,无需创建任何对象就可以访问
- int main(int argc, char const *argv[])
- {
- A::fun();
- return 0;
- }
8.
类的静态成员变量必须在类外进行初始化才能被使用,否则报错,例如:
- class A
- {
- private:
- int a;
- int b;
- static int c;
- public:
- static void fun();
- };
- int A::c = 0; //静态成员变量初始化
可以使用对象操作静态成员变量,也可以使用类本身,前提是静态成员变量是公有的,如下程序:
- #include
- #include
- using namespace std;
- class A
- {
- private:
- int a;
- int b;
- public:
- static int c;
- static void fun()
- {
- cout << c << endl;
- }
- };
- int A::c = 0;
- int main(int argc, char const *argv[])
- {
- A a;
- a.c = 0;
- A::c = 5;
- a.fun();
- return 0;
- }

友元函数和友元类(friend)
按 c++的封装性来说,最主要的目的就是确保数据的安全,实现信息的隐藏,从原则上来 说,类的私有成员和保护成员,在类的外部是不能直接访问的,但是,有一个例外,这个就是友元(friend),友元就是在类的声明中,用关键字 friend 修饰的函数或者类,友元授予一个函数或者一个类特权,允许他们能直接访问本类的隐藏信息.
友元函数
友元函数不是类的成员函数,但是仍可以访问该类的私有成员。友元函数可以是一个外部函数,也可以是另一个类的成员函数。
友元函数可以访问类中的私有成员和其他数据,但是访问不可直接使用数据成员,需要通
过对对象进行引用,所以友元函数必须有一个参数用来传递对象的地址或引用
当友元函数是全局类外函数时在类中声明的格式:
friend 返回值类型 函数名(参数列表);
如下是当类外函数做友元的示例:
- #include
- #include
- using namespace std;
- class A; //对 A 进行超前声明,不然 sun 参数找不到
- int sun(int a,int b,A& c);
- class A
- {
- private:
- int x;
- int y;
- public:
- A(int a,int b):x(a),y(b){}
- ~A(){}
- friend int sun(int a,int b,A& c);
- };
- int sun(int a,int b, A& c)
- {
- c.x = a;
- c.y = b;
- return c.x+c.y;
- }
- int main(int argc, char const *argv[])
- {
- A a(2,4);
- sun(1,2,a);
- return 0;
- }
特别要注意的是,在对函数声明时,一定要对类进行超前声明,因为在对函数声明中有个参数是这个类,不进行超前声明函数会找不到这个类的定义。还有在友元函数在调用上同一般函数一样,不必通过对对象进行引用。
当友元函数是另外一个类中的成员函数时在类中声明的格式:
friend 返回值类型 类名::函数名(参数列表);
如下是当其他类的成员函数做友元的示例:
- #include
- #include
- using namespace std;
- class A; //对 A 进行超前声明,不然 sun 参数找不到
- class B
- {
- private:
- int i;
- char j;
- public:
- B(int a,char c)
- {
- i = a;
- j = c;
- }
- ~B(){}
- int fun(A *rm);
- };
- class A
- {
- private:
- int x;
- int y;
- public:
- A(int a,int b):x(a),y(b){}
- ~A(){}
- friend int B::fun(A* rm); //友元
- };
- int B::fun(A *rm)
- {
- i = rm->x + rm->y;
- j = 'a';
- return i;
- }
- int main(int argc, char const *argv[])
- {
- A a(2,4);
- B b(2,'A');
- b.fun(&a);
- return 0;
- }
友元类
可以将一个类定义为另一个类的友元,这样,被定义为友元的类中的函数就可以访问另一个类中的所有内容了,
这样是不安全的。最好的方法就是哪个需要访问,就设置为友元函数即可。
当其他类是友元类时在类中声明的格式:
friend 类名;
如下是当其他类做友元的示例:
- #include
- #include
- using namespace std;
- class A; //对 A 进行超前声明,不然 sun 参数找不到
- class B
- {
- private:
- int i;
- char j;
- public:
- B(int a,char c)
- {
- i = a;
- j = c;
- }
- ~B(){}
- int fun(A &a);
- };
- class A
- {
- private:
- int x;
- int y;
- friend B;
- public:
- A(int a,int b):x(a),y(b){}
- ~A(){}
- };
- int B::fun(A &a)
- {
- i = a.x + a.y;
- return i;
- }
- int main(int argc, char const *argv[])
- {
- A a(2,4);
- B b(2,'A');
- b.fun(a);
- return 0;
- }
友元注意事项
1.友元关系是单向的,如 类 A 声明它有一个朋友 B,即 B 是 A 的朋友,反过来,A 不一定是 B 的朋友
2. 友元关系是不可传递的,如 A 是 B 的朋友,B 是 C 的朋友,A 不一定是 C 的朋友
3.
一个函数可以是多个类友元函数。