目录
(1)类的封装
(2)对象的构造和析构
定义和调用说明
有参数的构造函数,构造函数重载,拷贝构造函数
匿名对象的去和留
类中嵌套对象的初始化与对象初始化列表
类中const常量初始化与对象初始化列表
(3)new和delete用法
(4)类的声明和实现分开
(5)static与静态成员变量(函数)
静态成员变量
静态成员函数
(6)c++编译器对成员变量和函数的处理机制
内存占用机制
this指针
⽤const限定this指针
(1)类的封装
封装的多层含义
- 把属性和⽅法进⾏封装
- 对属性和⽅法进⾏访问控制
- 类的内部和类的外部
- 类的访问控制关键字
- public: 修饰的成员变量和函数,可以在类的内部和类的外部访问
- private: 修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问
- protected: 修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问,⽤ 在继承⾥⾯
案例:设计⽴⽅体类(cube),求出⽴⽅体的⾯积和体积。求两个⽴⽅体,是否相等(全局函数和成员函数都要实现)。
#include <iostream>
class Cube
{
public:
void setA(int a){m_a = a;}
void setB(int b){m_b = b;}
void setC(int c){m_c = c;}
void setABC(int a = 0, int b = 0, int c = 0)
{
m_a = a;
m_b = b;
m_c = c;
}
int getA(){return m_a;}
int getB(){return m_b;}
int getC(){return m_c;}
public:
int getV()
{
m_v = m_a * m_b * m_c;
return m_v;
}
int getS()
{
m_s = 2 * (m_a * m_b + m_a * m_c + m_b * m_c);
return m_s;
}
int judgeCube(Cube &v1, Cube &v2) //不建议这样写
{
if ((v1.getA() == v2.getA())
&& (v1.getB() == v2.getB())
&& (v1.getC() == v2.getC()))
return 1;
return 0;
}
int judgeCube(Cube &v2) //成员函数 函数重载
{
if ((m_a == v2.getA())
&& (m_b == v2.getB())
&& (m_c == v2.getC()))
return 1;
return 0;
}
private:
int m_a;
int m_b;
int m_c;
int m_v;
int m_s;
};
// 全局函数 PK 成员函数
// 1相等 0不相等
int judgeCube(Cube &v1, Cube &v2)
{
if ((v1.getA() == v2.getA())
&& (v1.getB() == v2.getB())
&& (v1.getC() == v2.getC()))
return 1;
return 0;
}
int main()
{
Cube v1, v2;
v1.setABC(1, 2, 3);
std::cout << v1.getS() << std::endl;
std::cout << v1.getV() << std::endl;
// 用成员函数判断两个立方体是否相等
v2.setABC(1, 2, 4);
if (v1.judgeCube(v1, v2) == 1)
std::cout << "v1 = v2" << std::endl;
else
std::cout << "v1 != v2" << std::endl;
// 用成员函数判断两个立方体是否相等
v1.setABC(1, 2, 4);
if (v1.judgeCube(v2) == 1)
std::cout << "v1 = v2" << std::endl;
else
std::cout << "v1 != v2" << std::endl;
// 用全局函数判断两个立方体是否相等
if (judgeCube(v1, v2) == 1)
std::cout << "v1 = v2" << std::endl;
else
std::cout << "v1 != v2" << std::endl;
return 0;
}
(2)对象的构造和析构
定义和调用说明
1 构造函数定义
- C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。
- 构造函数默认是无参数的,当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。也可以在定义时自行设计有参数和函数体。
- 没有任何返回类型的声明。
2 构造函数的调⽤
- ⾃动调⽤:⼀般情况下C++编译器会⾃动调⽤构造函数
- ⼿动调⽤:在⼀些情况下则需要⼿⼯调⽤构造函数
3 析构函数定义及调⽤
- C++中的类可以定义⼀个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
- 语法:~ClassName()
- 析构函数没有参数也没有任何返回类型的声明
- 析构函数在对象销毁时⾃动被调⽤
- 析构函数调⽤机制:C++编译器⾃动调⽤
有参数的构造函数,构造函数重载,拷贝构造函数
⼆个特殊的构造函数:
- 默认无参构造函数。当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。
- 默认拷贝构造函数。当类中没有定义拷⻉构造函数时,编译器默认提供⼀个默认拷⻉构造函数,简单的进⾏成员变量的值复制。
示例代码
#include <iostream>
#include <cstring>
class Test
{
public:
Test() //无参数构造函数
{
age = 0;
strcpy(name, "tom");
}
Test(int age) //有参数构造函数 //构造函数重载
{
this->age = age;
strcpy(name, "tom");
}
Test(int age, char *name) //有参数构造函数 //构造函数重载
{
this->age = age;
strcpy(this->name, name);
}
Test(const Test &obj) // 拷贝构造函数
{
this->age = obj.age;
this->name = obj.name;
}
~Test() //析构函数
{
std::cout << "object destroyed" << std::endl;
}
public:
void printT()
{
std::cout << "name = " << name << std::endl;
std::cout << "age = " << age << std::endl;
std::cout << "---------------" << std::endl;
}
private:
char *name = new char[20];
int age;
};
int main()
{
// 1.c++编译器自动的调用构造函数
Test t0; // 自动调用无参数构造函数
t0.printT();
char name1[] = "hello";
Test t1(10, name1); // 自动调用参数构造函数
t1.printT();
// 2.手动调用构造函数
char name2[] = "world";
Test t2 = Test(100, name2);
t2.printT();
strcpy(name2, "good");
Test t3 = Test(1000, name2); //t4对象的初始化
t2 = t3;
t2.printT();
// 3.对象赋值
t0 = t1;
std::cout << &t0 << "," << &t1 << std::endl; // 不同
t0.printT();
//对象的初始化 和 对象的赋值 是两个不同的概念
// 4.拷贝构造函数
Test t4 = Test(t3);
//Test t4 = t3; // 等价的方式 // 对象的初始化
t4.printT();
std::cout << &t3 << "," << &t4 << std::endl; // 不同
return 0;
}
编译运行
[root@centos1 test]# g++ main.cpp -o main -std=c++11
[root@centos1 test]# ./main
name = tom
age = 0
---------------
name = hello
age = 10
---------------
name = world
age = 100
---------------
name = good
age = 1000
---------------
0x7ffd41eaa4e0,0x7ffd41eaa4c0
name = hello
age = 10
---------------
name = good
age = 1000
---------------
0x7ffd41eaa490,0x7ffd41eaa480
object destroyed
object destroyed
object destroyed
object destroyed
object destroyed
[root@centos1 test]#
匿名对象的去和留
可以用函数返回对象,为匿名对象。关于匿名对象的去和留,结论如下:
- 返回的匿名对象,若没人接盘,则这个匿名对象构造后会直接析构。
- 返回的匿名对象,若赋值给另外一个同类型的对象,那么匿名对象在构造后会被析构。
- 返回的匿名对象,若直接来初始化另外一个同类型的对象,那么匿名对象会直接转成新的对象。
示例代码
#include <iostream>
class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx;
Y = yy;
std::cout << "Object Created\n";
}
Location(const Location &p) //拷贝构造函数
{
X = p.X;
Y = p.Y;
std::cout << "Object Copy" << std::endl;
}
~Location()
{
std::cout << "(" << X << "," << Y << ") "
<< "Object Destroyed" << std::endl;
}
public:
int GetX() { return X; }
int GetY() { return Y; }
private:
int X, Y;
};
void PrintLocation(Location p) //打印对象的属性
{
std::cout << "Funtion:" << p.GetX() << "," << p.GetY() << std::endl;
}
Location GenLocation() //返回一个匿名对象
{
Location A(1, 2);
return A;
}
// 匿名对象的去和留,关键看,返回时如何接盘
// = 在赋值和初始化这两种情况时有所不同
int main()
{
// (1)若返回的匿名对象,赋值给另外一个同类型的对象,那么匿名对象会被析构
std::cout << "--------B--------" << std::endl;
Location B; // 调用一次构造函数
B = GenLocation(); // 调用构造函数 // 然后调用析构函数
//(2)若返回的匿名对象,直接来初始化另外一个同类型的对象
// 那么匿名对象会直接转成新的对象
std::cout << "--------C--------" << std::endl;
Location C = GenLocation(); // 调用一次构造函数
//(3)没人接盘 //这个匿名对象构造后会直接析构
std::cout << "----------------" << std::endl;
GenLocation(); // 调用一次构造函数 // 然后调用析构函数
std::cout << "--------end--------" << std::endl;
// 程序结束时
// B调用一次析构函数
// C调用一次析构函数
return 0;
}
编译运行
[root@localhost test]# g++ main.cpp -o main
[root@localhost test]# ./main
--------B--------
Object Created
Object Created
(1,2) Object Destroyed
--------C--------
Object Created
----------------
Object Created
(1,2) Object Destroyed
--------end--------
(1,2) Object Destroyed
(1,2) Object Destroyed
[root@localhost test]#
类中嵌套对象的初始化与对象初始化列表
情况描述:如果我们设计的类中有一个成员,它本身是另一个类,而且这个成员构造函数只有带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
解决办法:C++在构造函数中提供初始化列表对成员变量进行初始化。
语法规则:作用域::类名(参数1, 参数2, ...):成员类名1(实参值a, 实参值b,...), 成员类名2((实参值m, 实参值n,...)
构造函数和析构函数调用顺序:
- 构造函数顺序:先执行被组合对象的构造函数 。如果组合对象有多个,按照定义顺序,,而不是按照初始化列表的顺序。初始化列表先于构造函数的函数体执行。
- 析构函数 : 和构造函数的调用顺序相反
示例:在B类中,组合了一个 A类对象,A类设计了构造函数,但构造函数只有带参的构造函数,根据构造函数的调用规则,必须要调用A类的带参构造函数。
示例代码
#include <iostream>
#include <cstring>
class A
{
public:
A(int _a)
{
a = _a;
std::cout << "generate A " << a << std::endl;
}
~A()
{
std::cout << "destroy A " << a << std::endl;
}
protected:
private:
int a;
};
class B
{
public:
//B(int _b1, int _b2): a1(_b1), a2(_b2)
B(int _b1, int _b2): a1(1), a2(2)
{
b1 = _b1;
b2 = _b2;
std::cout << "generate B " << std::endl;
}
B(int _b1, int _b2, int m, int n):a1(m), a2(n)
{
b1 = _b1;
b2 = _b2;
std::cout << "generate B " << std::endl;
}
~B()
{
std::cout << "destroy B " << std::endl;
}
protected:
private:
int b1;
int b2;
A a2;
A a1;
};
void objplay()
{
B(1, 2);
}
int main()
{
objplay();
return 0;
}
运行结果
generate A 2
generate A 1
generate B
destroy B
destroy A 1
destroy A 2
类中const常量初始化与对象初始化列表
当类成员中含有⼀个const对象时,或者是⼀个引⽤时,他们也必须要通过成员初始化列表进⾏初始化,因为这两种对象要在声明后⻢上初始化,⽽在构造函数中,做的是对他们的赋值,这样是不被允许的。
示例代码
#include <iostream>
#include <cstring>
class A
{
public:
A(int _a)
{
a = _a;
std::cout << "generate A " << a << std::endl;
}
~A()
{
std::cout << "destroy A " << a << std::endl;
}
public:
int geta(){return a;}
protected:
private:
int a;
};
class B
{
public:
B(int _b1, int _b2): a1(_b1), a2(_b2), c(_b2)
// B(int _b1, int _b2): a1(1), a2(2), c(0)
{
b1 = _b1;
b2 = _b2;
std::cout << "generate B " << std::endl;
}
B(int _b1, int _b2, int m, int n):a1(m), a2(n), c(0)
{
b1 = _b1;
b2 = _b2;
std::cout << "generate B " << std::endl;
}
~B()
{
std::cout << "destroy B " << std::endl;
}
public:
void print()
{
std::cout << "---------" << std::endl;
std::cout << b1 << std::endl;
std::cout << b2 << std::endl;
std::cout << a2.geta() << std::endl;
std::cout << a1.geta() << std::endl;
std::cout << c << std::endl;
}
protected:
private:
int b1;
int b2;
A a2;
A a1;
const int c;
};
void objplay()
{
B obj_b1 = B(1, 2);
obj_b1.print();
B obj_b2(3, 4);
obj_b2.print();
}
int main()
{
objplay();
return 0;
}
运行结果
generate A 2
generate A 1
generate B
---------
1
2
2
1
2
generate A 4
generate A 3
generate B
---------
3
4
4
3
4
destroy B
destroy A 3
destroy A 4
destroy B
destroy A 1
destroy A 2
(3)new和delete用法
在软件开发过程中,常常需要动态地分配和撤销内存空间。在C语⾔中是利⽤库函数malloc和 free来分配和撤销内存空间的。C++提供了较简便⽽功能较强的运算符new和delete来取代 malloc和free函数。
注意:new和delete是运算符,不是函数,因此执⾏效率⾼。
虽然为了与C语⾔兼容,C++仍保留malloc和free函数,但建议⽤户不⽤malloc和free函数,⽽ ⽤new和delete运算符。
new用法举例:
- new int; //开辟⼀个存放整数的存储空间,返回⼀个指向该存储空间的地址(即指针)
- new int(100); //开辟⼀个存放整数的空间,并指定该整数的初值为100,返回⼀个指向该 存储空间的地址
- new char[10]; //开辟⼀个存放字符数组(包括10个元素)的空间,返回⾸元素的地址
- new int[5][4]; //开辟⼀个存放⼆维整型数组(⼤⼩为5*4)的空间,返回⾸元素的地址
- float *p=new float(3.14159); //开辟⼀个存放单精度数的空间,并指定该实数的初值为 3.14159,将返回的该空间的地址赋给指针变量p
- Box *pt; //定义⼀个指向Box类对象的指针变量pt
- pt=new Box; //在pt中存放了新建对象的起始地址
- Box *pt = new Box ;
- Box *pt=new Box(12,15,18);
注意⽤new分配数组空间时不能指定初值。如果由于内存不⾜等原因⽽⽆法正常分配空间,则new会 返回⼀个空指针NULL,⽤户可以根据该指针的值判断分配空间是否成功。
delete用法:
若P是⼀个指向数组的指针,则delete p表示清空内存⾸地址,delete[] p表示清空整个数组 对应的堆中内存空间。
malloc-free和new-delete区别:
- malloc不会调⽤类的构造函数,new能执⾏类型构造函数
- free不会调⽤类的析构函数,delete操作符 能执⾏类的析构函数
示例代码
#include <iostream>
#include <stdlib.h>
class Test
{
public:
Test(int _a)
{
a = _a;
std::cout << "generate A" << std::endl;
}
~Test()
{
std::cout << "destroy A" << std::endl;
}
protected:
private:
int a;
};
int main()
{
// 1.基础类型
// 用malloc这个函数
int *p1 = (int *)malloc(sizeof(int));
*p1 = 10;
free(p1);
// 用new这个运算符
int *p2 = new int;
*p2 = 20;
free(p2);
int *p3 = new int(30);
delete p3;
// 2.数组
// malloc
int *p4 = (int *)malloc(sizeof(int) * 10); // int array[10];
p4[0] = 1;
free(p4);
// new
int *pArray = new int[10];
pArray[1] = 2;
delete[] pArray;
char *pArray2 = new char[25];
delete[] pArray2;
// 3.给对象分配堆内内存空间
std::cout << "---------malloc------------" << std::endl;
Test *testp1 = (Test *)malloc(sizeof(Test));
std::cout << "---------free------------" << std::endl;
free(testp1);
std::cout << "----------new-----------" << std::endl;
Test *testp2 = new Test(1);
std::cout << "----------delete-----------" << std::endl;
delete testp2;
return 0;
}
运行结果
---------malloc------------
---------free------------
----------new-----------
generate A
----------delete-----------
destroy A
(4)类的声明和实现分开
设计.h和.cpp,一个声明,一个实现。为了避免同⼀个头⽂件被include多次带来重复编译,头⽂件要么用#ifndef 宏名 #define 宏名 #endif编写头文件,要么用#pragma once。#ifndef由语⾔⽀持移植性更好,推荐使用。
案例:带有数组成员的类
myarrray.h
#ifndef _MYARRAY_H_
#define _MYARRAY_H_
class Array
{
public:
Array(int length);
Array(const Array &obj); //加了const更好
~Array();
public:
void setdata(int index, int value);
int getdata(int index);
int getLenth();
private:
int m_length;
int *m_space;
};
#endif
myarray.cpp
#include <iostream>
#include "myarray.h"
//作用域不要忘写
//构造函数
Array::Array(int length)
{
if (length < 0){length = 0;}
m_length = length;
m_space = new int[length];
// this->m_length = length;
// this->m_space = new int[length];
}
//构造函数的重载
Array::Array(const Array &obj)
{
m_length = obj.m_length;
m_space = new int[m_length];
for (int i = 0; i < m_length; i++)
m_space[i] = obj.m_space[i];
}
//析构函数
Array::~Array()
{
if (m_space != NULL)
{
delete[] m_space;
m_space = NULL;
m_length = -1;
}
}
//成员函数
void Array::setdata(int index, int value)
{
if (0 <= index && index <= m_length)
m_space[index] = value;
}
int Array::getdata(int index)
{
if (0 <= index && index <= m_length)
return m_space[index];
return -1;
}
int Array::getLenth()
{
return m_length;
}
main.cpp
#include <iostream>
#include "myarray.h"
int main()
{
Array array1(4);
for (int i = 0; i < array1.getLenth(); i++)
{
array1.setdata(i, i + 100);
std::cout << array1.getdata(i) << "\t";
}
std::cout << std::endl;
Array array2 = array1;
for (int i = 0; i < array2.getLenth(); i++)
{
std::cout << array2.getdata(i) << "\t";
}
std::cout << std::endl;
return 0;
}
编译运行
[root@centos1 test]# g++ -o main main.cpp myarray.cpp
[root@centos1 test]# ./main
100 101 102 103
100 101 102 103
[root@centos1 test]#
(5)static与静态成员变量(函数)
静态成员变量
思考:每个变量,拥有属性。有没有⼀些属性,归所有对象拥有?
把⼀个类的成员修饰为 static 时,这个类⽆论有多少个对象被创建,这些同类对象共享这个 static 成员。
- 静态成员在类的内部声明,但要在类的外部初始化。静态成员局部于类,它不是对象成员。
- 静态不初始化时,只要不调⽤这个静态成员编译就不会报错,但只有调⽤时才会报错。
- 静态成员只有在初始化后,才会分配内存空间。
#include <iostream>
class counter
{
public:
static int num; //声明静态数据成员
public:
void setnum(int i) { num = i; } //成员函数访问静态数据成员
void shownum() { std::cout << num << std::endl; }
};
// 在类的外部初始化静态数据成员
// 只能初始化一次
int counter::num = 0;
int main()
{
counter a, b;
//调用成员函数访问私有静态数据成员
a.shownum(); //0
b.shownum(); //0
//只要有一个对象改变了静态成员的值
//则所有对象调用该静态成员时的值也会变
a.setnum(10);
a.shownum(); //10
b.shownum(); //10
//若静态数据成员是public的
//还可以直接通过类访问静态成员
std::cout << counter::num << std::endl;
return 0;
}
静态成员函数
用static修饰成员函数,成为静态函数。
对于静态成员函数,是全部对象公有函数,提供不依赖于类数据结构的共同操作,它没有this指针。
- 在类外调⽤静态成员函数⽤ “类名::静态函数”作限定词,或通过对象调⽤。
-
注意静态函数中,不能使用普通成员变量,也不能调用普通成员函数,只能使用静态成员变量
#include <iostream>
class Worker
{
public:
void company_age_add(){company_age++;}
void print_company_age1()
{
std::cout << "normal print: company_age=" << company_age << std::endl;
}
static void print_company_age2() //静态成员函数
{
std::cout << "static print: company_age=" << company_age << std::endl;
// std::cout << "age:" << age << std::endl;
// error C2597: 对非静态成员“Worker::age”的非法引用
// 静态函数中 不能使用 普通成员变量 普通成员函数
}
protected:
private:
int age;
int salary;
static int company_age; //静态成员变量
};
int Worker::company_age = 10;
int main()
{
Worker w1, w2, w3;
// 1.普通成员函数调用静态成员变量
w1.print_company_age1(); // normal print: company_age=10
w2.company_age_add();
w3.print_company_age1(); // normal print: company_age=11
// 2.调用静态成员函数
// 第一种:用对象.
w3.print_company_age2(); // static print: company_age=11
// 第二种:类::
Worker::print_company_age2(); // static print: company_age=11
return 0;
}
(6)c++编译器对成员变量和函数的处理机制
内存占用机制
C++类对象中的成员变量和成员函数是分开存储的。
- 成员变量:
- 普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对⻬⽅式
- 静态成员变量:存储于全局数据区中
- 成员函数:存储于代码段中。
#include <iostream>
#include <stdio.h>
class C1
{
public:
int i; // 4个字节
int j; // 4个字节
int k; // 4个字节
protected:
private:
}; //共12个字节
class C2
{
public:
int i;
int j;
int k;
static int m;
public:
int getK() const { return k; }
void setK(int val) { k = val; }
protected:
private:
};
struct S1
{
int i;
int j;
int k;
}; // 12
struct S2
{
int i;
int j;
int k;
static int m;
}; // 12
int main()
{
printf("c1:%d \n", sizeof(C1)); //12
printf("c2:%d \n", sizeof(C2)); //12
printf("s1:%d \n", sizeof(S1)); //12
printf("s2:%d \n", sizeof(S2)); //12
return 0;
}
this指针
C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。
静态成员函数与普通成员函数的区别:静态成员函数不包含指向具体对象的指针,普通成员函数 包含⼀个指向具体对象的指针。
#include <iostream>
class Test
{
public:
Test(int a, int b)
{
this->a = a;
this->b = b;
}
void print1(int a) //这个a是函数形参的a
{
std::cout << "----------print1----------" << std::endl;
for (int i = 0; i < a; i++)
{
std::cout << "a: " << this->a << std::endl; //打印对象的属性a的值
std::cout << "b: " << this->b << std::endl;
}
}
void print2()
{
std::cout << "----------print2----------" << std::endl;
// 当函数形参没有与成员变量同名的形参时
// 函数内部可以不用this,直接访问成员变量
std::cout << "a: " << a << std::endl; //打印对象的属性a的值
std::cout << "b: " << b << std::endl;
}
protected:
private:
int a;
int b;
};
int main()
{
Test t1(1, 2);
t1.print1(3);
t1.print2();
return 0;
}
⽤const限定this指针
C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。
将const写在成员函数处,比如void OperateVar(int a, int b) const,将会对this指针和this下的成员进行修饰,只有只读属性,使得this和this成员不能被改变。
注意:
- const 无论写在函数什么位置,都没有关系,像下面这样都可以
-
void OperateVar(int a, int b) const
-
const void OperateVar(int a, int b)
-
void const OperateVar(int a, int b)
-
-
const修饰的是谁?
-
const修饰的是形参a吗?不是
-
const修饰的是属性this->a this->b
-
const修饰的是this指针所指向的内存空间, 修饰的是this指针
-
#include <iostream>
class Test
{
public:
Test(int a, int b) //---> Test(Test *this, int a, int b)
{
this->a = a;
this->b = b;
}
void printT()
{
std::cout << "a: " << a << "\t";
std::cout << "b: " << this->b << std::endl;
}
void OperateVar(int a, int b) const
// void const OperateVar(int a, int b)
// const void OperateVar(int a, int b)
{
a = 100;
// this->a = 100; //因为const,会报错
// this->b = 200; //因为const,会报错
// this = 0x11; //因为const,会报错
std::cout << "a: " << this->a << "\t";
std::cout << "b: " << this->b << std::endl;
}
protected:
private:
int a;
int b;
};
int main()
{
int *m_space = new int[0];
if (m_space == NULL)
{
return -1;
}
Test t1(1, 2);
t1.printT();
//a: 1 b: 2
t1.OperateVar(100, 200);
//a: 1 b: 2
return 0;
}
end