本文参考自C++11 std::move的UT测试
查看原文: 原文地址
给出C++代码涉及到静态变量Static, 右值引用std::move, gmock, gtest等相关内容。
1. 静态变量Static
1.1 Static类成员变量
#include <iostream>
class StaticTest
{
public:
void show() const {std::cout << age << std::endl;}
void changeAge() {age = age + 1;}
private:
static int age;
};
int StaticTest::age = 1;
int main()
{
StaticTest test1;
test1.show();
test1.changeAge();
test1.show();
test1.changeAge();
test1.show();
return 0;
}
Static类成员变量独立于类对象存在,在类中的public、protected或private中存在都没有关系;
Static类成员变量独立于类对象存在,所有该类对象共同访问static变量,因此需要在类外初始化;
Static类成员变量存储在内存中的全局数据区,在声明它的整个文件中可见;
2. 右值引用和std::move
2.1 右值引用
#include <iostream>
int main()
{
double s1 = 3.14;
double &r1 = s1;
const double &r2 = s1;
std::cout << s1 << " " << r1 << " " << r2 << std::endl;
double &&m1 = 3.14;
const double &n1 = 3.14;
// double &n2 = 3.14; error: cannot bind non-const lvalue reference of type 'double&' to an rvalue of type 'double'
std::cout << m1 << " " << n1 << " " << std::endl;
double p = 3.14;
// double &&q = p; error: cannot bind rvalue reference of type 'double&&' to lvalue of type 'double'
std::cout << p << std::endl;
double &&x = 3.14;
// double &&y =x; error: cannot bind rvalue reference of type 'double&&' to lvalue of type 'double'
std::cout << x << std::endl;
return 0;
}
左值相当于地址值,右值相当于数据值;
右值引用指向将要被销毁的对象;
不能将右值引用绑定到一个变量(左值),即使这个变量是右值引用类型;
2.2 std::move
#include <iostream>
#include <cstring>
class WithoutMove
{
public:
void test()
{
int a = 1;
std::cout << a << " " << &a << std::endl;
int b = a;
std::cout << b << " " << &b << std::endl;
std::cout << a << " " << &a << std::endl;
}
};
class WithMove
{
public:
void test()
{
int a = 1;
std::cout << a << " " << &a << std::endl;
int&& b = std::move(a);
std::cout << b << " " << &b << std::endl;
std::cout << a << " " << &a << std::endl;
}
};
class WithMoveConst
{
public:
void test()
{
std::string&& b = std::move("123");
std::cout << b << " " << &b << std::endl;
}
};
int main()
{
WithoutMove withoutMove;
withoutMove.test();
std::cout << std::endl;
WithMove withMove;
withMove.test();
std::cout << std::endl;
WithMoveConst withMoveConst;
withMoveConst.test();
}
C++ 11 中std::move 使用了模板:
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
从模板可以看出,使用 std::move 时传入的实参应为常量或即将消失的对象,如果不是的话,则作为引用传入move函数中;
3. std::move的UT测试
代码思路: 使用GMock和Gtest测试一个支持Move操作的类和不支持Move操作的类,了解C++ 11 中的Move机制和拷贝构造函数、赋值运算符、移动构造函数、移动赋值运算符等类成员函数的实现细节;
代码结构如下:
ILogger.h
#pragma once
#include <cstring>
#include <gmock/gmock.h>
class ILogger
{
public:
virtual void logDefaultCtor(std::string str) = 0;
virtual void logCopyCtor(std::string str) = 0;
virtual void logCopyAssignment(std::string str) = 0;
virtual ~ILogger() = default;
virtual void logMoveCtor(std::string str) = 0;
virtual void logMoveAssignment(std::string str) = 0;
};
class MockLogger: public ILogger
{
public:
MOCK_METHOD1(logDefaultCtor, void(std::string));
MOCK_METHOD1(logMoveCtor, void(std::string));
MOCK_METHOD1(logCopyAssignment, void(std::string));
MOCK_METHOD1(logMoveAssignment, void(std::string));
MOCK_METHOD1(logCopyCtor, void(std::string));
};
MyString.h
#pragma once
#include <cstring>
#include <memory>
#include "ILogger.h"
class MyString
{
public:
MyString(): data(nullptr){}
MyString(const char* str)
{
allocAndCopy(str);
}
MyString(const MyString& rhv)
{
allocAndCopy(rhv.data);
}
MyString& operator=(const MyString& rhv)
{
if(this != &rhv and data != nullptr)
{
recycle();
}
if(this != &rhv)
{
allocAndCopy(rhv.data);
}
return *this;
}
static int getAllocCounter()
{
return alloc_cnt;
}
static int getDeleteCounter()
{
return delete_cnt;
}
static void resetCounter()
{
alloc_cnt = 0;
delete_cnt = 0;
}
~MyString()
{
if(data)
{
recycle();
}
}
std::string getString()
{
if(not data)
{
return std::string("");
}
return std::string(data);
}
friend std::ostream& operator<<(std::ostream& os, const MyString& str)
{
if(not str.data)
{
os << str.data;
}
return os;
}
static void displayStatistic()
{
std::cout << "Total" << alloc_cnt <<"Memory blocks allocated, "
<< "and " << delete_cnt << " recycled." << std::endl;
}
protected:
char* data = nullptr;
void allocAndCopy(const char* str)
{
alloc_cnt++;
data = new char[strlen(str) + 1];
strcpy(data, str);
}
static int alloc_cnt;
static int delete_cnt;
void inline recycle()
{
delete_cnt++;
delete [] data;
}
using Logger = std::shared_ptr<ILogger>;
};
MyStringWithMove.h
#pragma once
#include <cstring>
#include "ILogger.h"
#include "MyString.h"
class MyStringWithMove: public MyString
{
public:
MyStringWithMove():MyString()
{
logger->logDefaultCtor("");
}
MyStringWithMove(const char* str): MyString(str)
{
logger->logDefaultCtor(str);
}
MyStringWithMove(const MyStringWithMove& rhv): MyString(rhv)
{
logger->logCopyCtor(this->getString());
}
MyStringWithMove& operator=(const MyStringWithMove& rhv) noexcept
{
dynamic_cast<MyString &>(*this) = dynamic_cast<const MyString&>(rhv);
logger->logCopyAssignment(this->getString());
return *this;
}
MyStringWithMove(MyStringWithMove&& rhv)
{
data = rhv.data;
rhv.data = nullptr;
logger->logMoveCtor(this->getString());
}
MyStringWithMove& operator=(MyStringWithMove&& rhv)
{
if(this != &rhv and data != nullptr)
{
recycle();
}
data = rhv.data;
rhv.data = nullptr;
logger->logMoveAssignment(this->getString());
return *this;
}
private:
static Logger logger;
};
MyStringWithoutMove.h
#pragma once
#include <cstring>
#include "ILogger.h"
#include "MyString.h"
class MyStringWithoutMove: public MyString
{
public:
MyStringWithoutMove(): MyString()
{
logger->logDefaultCtor("");
}
MyStringWithoutMove(const char* str): MyString(str)
{
logger->logDefaultCtor(str);
}
MyStringWithoutMove(const MyStringWithoutMove& rhv): MyString(rhv)
{
logger->logCopyCtor(this->getString());
}
MyStringWithoutMove& operator=(const MyStringWithoutMove& rhv)
{
dynamic_cast<MyString&>(*this) = dynamic_cast<const MyString&>(rhv);
logger->logCopyAssignment(this->getString());
return *this;
}
private:
static Logger logger;
};
main.cpp
#include "../include/ILogger.h"
#include "../include/MyString.h"
#include "../include/MyStringWithoutMove.h"
#include "../include/MyStringWithMove.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
int MyString::alloc_cnt = 0;
int MyString::delete_cnt = 0;
std::shared_ptr<testing::StrictMock<MockLogger>> mockLogger = std::make_shared<testing::StrictMock<MockLogger>>();
MyString::Logger MyStringWithMove::logger = mockLogger;
MyString::Logger MyStringWithoutMove::logger = mockLogger;
class MyStringWithMoveTest: public ::testing::Test
{
protected:
virtual void SetUp() final
{
MyString::resetCounter();
}
virtual void TearDown() final
{
if(MyString::getAllocCounter() == MyString::getDeleteCounter())
{
std::cout << "same alloc and delete number" << std::endl;
}
else
{
std::cout << "different alloc and delete number" << std::endl;
}
}
std::shared_ptr<MockLogger> mockLogger = std::make_shared<MockLogger>();
};
class MyStringWithoutMoveTest: public ::testing::Test
{
protected:
virtual void SetUp() final
{
MyString::resetCounter();
}
virtual void TearDown() final
{
if(MyString::getAllocCounter() == MyString::getDeleteCounter())
{
std::cout << "same alloc and delete number" << std::endl;
}
else
{
std::cout << "different alloc and delete number" << std::endl;
}
}
std::shared_ptr<MockLogger> mockLogger = std::make_shared<MockLogger>();
};
TEST_F(MyStringWithMoveTest, MoveNormalValue)
{
EXPECT_CALL(*mockLogger, logDefaultCtor("111"));
EXPECT_CALL(*mockLogger, logMoveCtor("111"));
MyStringWithMove str1("111");
MyStringWithMove str2(std::move(str1));
EXPECT_EQ(str1.getString(), "");
EXPECT_EQ(str2.getString(), "111");
EXPECT_EQ(MyString::getAllocCounter(), 1);
mockLogger->logDefaultCtor("111");
mockLogger->logMoveCtor("111");
}
TEST_F(MyStringWithoutMoveTest, MoveNormalValue)
{
EXPECT_CALL(*mockLogger, logDefaultCtor("111"));
EXPECT_CALL(*mockLogger, logCopyCtor("111"));
MyStringWithoutMove str1 = "111";
MyStringWithoutMove str2 = std::move(str1);
EXPECT_EQ(str1.getString(), "111");
EXPECT_EQ(str2.getString(), "111");
EXPECT_EQ(MyString::getAllocCounter(), 2);
mockLogger->logDefaultCtor("111");
mockLogger->logCopyCtor("111");
}
TEST_F(MyStringWithMoveTest, CtorMoveValue)
{
MyStringWithMove str1 = "111";
MyStringWithMove str2 = str1;
MyStringWithMove str3;
str3 = str1;
EXPECT_EQ(str1.getString(), "111");
EXPECT_EQ(str2.getString(), "111");
EXPECT_EQ(str3.getString(), "111");
EXPECT_EQ(MyString::getAllocCounter(), 3);
}
TEST_F(MyStringWithMoveTest, RValueMove)
{
MyStringWithMove str;
str = "111";
EXPECT_EQ(str.getString(), "111");
}
TEST_F(MyStringWithoutMoveTest, RValueWithoutMove)
{
MyStringWithoutMove str1;
str1 = "111";
EXPECT_EQ(str1.getString(), "111");
}
运行结果:
推荐文章:
C++ static 类成员,static类成员函数: https://blog.youkuaiyun.com/u014453898/article/details/64124269/
C++ Primer笔记 - 理解std::move : https://www.cnblogs.com/zoneofmine/p/7440577.html
Linux大棚版gtest官网教程译文: http://roclinux.cn/?p=2873
小亮同学的google mock分享: http://www.cnblogs.com/welkinwalker/archive/2011/11/29/2267225.html